summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS8
-rw-r--r--async.c8
-rw-r--r--audio/Makefile.objs4
-rw-r--r--audio/alsaaudio.c179
-rw-r--r--audio/audio.c35
-rw-r--r--audio/audio_int.h7
-rw-r--r--audio/audio_template.h43
-rw-r--r--audio/coreaudio.c46
-rw-r--r--audio/dsound_template.h51
-rw-r--r--audio/dsoundaudio.c238
-rw-r--r--audio/esdaudio.c557
-rw-r--r--audio/fmodaudio.c685
-rw-r--r--audio/noaudio.c4
-rw-r--r--audio/ossaudio.c126
-rw-r--r--audio/paaudio.c105
-rw-r--r--audio/sdlaudio.c10
-rw-r--r--audio/spiceaudio.c5
-rw-r--r--audio/wavaudio.c42
-rw-r--r--audio/winwaveaudio.c717
-rw-r--r--block.c275
-rw-r--r--block/Makefile.objs1
-rw-r--r--block/blkdebug.c2
-rw-r--r--block/blkverify.c4
-rw-r--r--block/io.c71
-rw-r--r--block/qapi.c8
-rw-r--r--block/qcow2.c11
-rw-r--r--block/qcow2.h5
-rw-r--r--block/quorum.c51
-rw-r--r--block/raw-posix.c5
-rw-r--r--block/throttle-groups.c496
-rw-r--r--block/vmdk.c84
-rw-r--r--blockdev.c62
-rwxr-xr-xconfigure68
-rw-r--r--default-configs/s390x-softmmu.mak1
-rw-r--r--hmp.c10
-rw-r--r--hw/arm/boot.c20
-rw-r--r--hw/arm/pxa2xx.c248
-rw-r--r--hw/arm/pxa2xx_pic.c2
-rw-r--r--hw/arm/virt-acpi-build.c43
-rw-r--r--hw/arm/virt.c7
-rw-r--r--hw/core/nmi.c20
-rw-r--r--hw/display/exynos4210_fimd.c2
-rw-r--r--hw/intc/arm_gic.c2
-rw-r--r--hw/sd/pxa2xx_mmci.c68
-rw-r--r--hw/watchdog/Makefile.objs1
-rw-r--r--hw/watchdog/watchdog.c10
-rw-r--r--hw/watchdog/wdt_diag288.c122
-rw-r--r--include/block/block.h15
-rw-r--r--include/block/block_int.h26
-rw-r--r--include/block/throttle-groups.h46
-rw-r--r--include/hw/acpi/acpi-defs.h32
-rw-r--r--include/hw/nmi.h1
-rw-r--r--include/hw/watchdog/wdt_diag288.h36
-rw-r--r--include/monitor/monitor.h1
-rw-r--r--include/qapi/qmp/qdict.h4
-rw-r--r--include/qemu/queue.h6
-rw-r--r--include/qemu/throttle.h46
-rw-r--r--iothread.c11
-rw-r--r--monitor.c4
-rw-r--r--pc-bios/s390-ccw/Makefile2
-rw-r--r--qapi-schema.json6
-rw-r--r--qapi/block-core.json29
-rw-r--r--qemu-options.hx27
-rw-r--r--qmp-commands.hx3
-rw-r--r--qobject/qdict.c111
-rw-r--r--target-arm/cpu-qom.h8
-rw-r--r--target-arm/cpu.c29
-rw-r--r--target-arm/cpu.h2
-rw-r--r--target-arm/cpu64.c3
-rw-r--r--target-arm/helper.c212
-rw-r--r--target-arm/internals.h3
-rw-r--r--target-arm/kvm-consts.h4
-rw-r--r--target-arm/kvm32.c15
-rw-r--r--target-arm/kvm64.c15
-rw-r--r--target-arm/op_helper.c11
-rw-r--r--target-arm/psci.c19
-rw-r--r--target-arm/translate.c114
-rw-r--r--target-s390x/cpu.h1
-rw-r--r--target-s390x/kvm.c18
-rw-r--r--target-s390x/misc_helper.c29
-rw-r--r--tests/check-qdict.c67
-rwxr-xr-xtests/qemu-iotests/0511
-rw-r--r--tests/qemu-iotests/051.out3
-rwxr-xr-xtests/qemu-iotests/09393
-rwxr-xr-xtests/qemu-iotests/10310
-rw-r--r--tests/qemu-iotests/103.out5
-rwxr-xr-xtests/qemu-iotests/11960
-rw-r--r--tests/qemu-iotests/119.out11
-rwxr-xr-xtests/qemu-iotests/12065
-rw-r--r--tests/qemu-iotests/120.out15
-rw-r--r--tests/qemu-iotests/1242
-rwxr-xr-xtests/qemu-iotests/1284
-rw-r--r--tests/qemu-iotests/group2
-rw-r--r--tests/test-aio.c19
-rw-r--r--tests/test-throttle.c163
-rw-r--r--trace-events16
-rw-r--r--util/throttle.c81
97 files changed, 2829 insertions, 3246 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index e728d3a1d2..32c7ca4566 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -356,6 +356,13 @@ F: hw/misc/zynq_slcr.c
 F: hw/*/cadence_*
 F: hw/ssi/xilinx_spips.c
 
+ARM ACPI Subsystem
+M: Shannon Zhao <zhaoshenglong@huawei.com>
+M: Shannon Zhao <shannon.zhao@linaro.org>
+S: Maintained
+F: hw/arm/virt-acpi-build.c
+F: include/hw/arm/virt-acpi-build.h
+
 CRIS Machines
 -------------
 Axis Dev88
@@ -770,7 +777,6 @@ F: hw/net/rocker/
 Subsystems
 ----------
 Audio
-M: Vassili Karpov (malc) <av1474@comtv.ru>
 M: Gerd Hoffmann <kraxel@redhat.com>
 S: Maintained
 F: audio/
diff --git a/async.c b/async.c
index 46d9e639d7..77d080d6f5 100644
--- a/async.c
+++ b/async.c
@@ -280,6 +280,12 @@ static void aio_timerlist_notify(void *opaque)
     aio_notify(opaque);
 }
 
+static void aio_rfifolock_cb(void *opaque)
+{
+    /* Kick owner thread in case they are blocked in aio_poll() */
+    aio_notify(opaque);
+}
+
 AioContext *aio_context_new(Error **errp)
 {
     int ret;
@@ -297,7 +303,7 @@ AioContext *aio_context_new(Error **errp)
                            event_notifier_test_and_clear);
     ctx->thread_pool = NULL;
     qemu_mutex_init(&ctx->bh_lock);
-    rfifolock_init(&ctx->lock, NULL, NULL);
+    rfifolock_init(&ctx->lock, aio_rfifolock_cb, ctx);
     timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx);
 
     return ctx;
diff --git a/audio/Makefile.objs b/audio/Makefile.objs
index 26a0ac9507..481d1aa30e 100644
--- a/audio/Makefile.objs
+++ b/audio/Makefile.objs
@@ -5,13 +5,9 @@ common-obj-$(CONFIG_SPICE) += spiceaudio.o
 common-obj-$(CONFIG_COREAUDIO) += coreaudio.o
 common-obj-$(CONFIG_ALSA) += alsaaudio.o
 common-obj-$(CONFIG_DSOUND) += dsoundaudio.o
-common-obj-$(CONFIG_FMOD) += fmodaudio.o
-common-obj-$(CONFIG_ESD) += esdaudio.o
 common-obj-$(CONFIG_PA) += paaudio.o
-common-obj-$(CONFIG_WINWAVE) += winwaveaudio.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
 
-$(obj)/audio.o $(obj)/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
 sdlaudio.o-cflags := $(SDL_CFLAGS)
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index ed7655de86..6315b2d746 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -25,6 +25,7 @@
 #include "qemu-common.h"
 #include "qemu/main-loop.h"
 #include "audio.h"
+#include "trace.h"
 
 #if QEMU_GNUC_PREREQ(4, 3)
 #pragma GCC diagnostic ignored "-Waddress"
@@ -33,9 +34,28 @@
 #define AUDIO_CAP "alsa"
 #include "audio_int.h"
 
+typedef struct ALSAConf {
+    int size_in_usec_in;
+    int size_in_usec_out;
+    const char *pcm_name_in;
+    const char *pcm_name_out;
+    unsigned int buffer_size_in;
+    unsigned int period_size_in;
+    unsigned int buffer_size_out;
+    unsigned int period_size_out;
+    unsigned int threshold;
+
+    int buffer_size_in_overridden;
+    int period_size_in_overridden;
+
+    int buffer_size_out_overridden;
+    int period_size_out_overridden;
+} ALSAConf;
+
 struct pollhlp {
     snd_pcm_t *handle;
     struct pollfd *pfds;
+    ALSAConf *conf;
     int count;
     int mask;
 };
@@ -56,30 +76,6 @@ typedef struct ALSAVoiceIn {
     struct pollhlp pollhlp;
 } ALSAVoiceIn;
 
-static struct {
-    int size_in_usec_in;
-    int size_in_usec_out;
-    const char *pcm_name_in;
-    const char *pcm_name_out;
-    unsigned int buffer_size_in;
-    unsigned int period_size_in;
-    unsigned int buffer_size_out;
-    unsigned int period_size_out;
-    unsigned int threshold;
-
-    int buffer_size_in_overridden;
-    int period_size_in_overridden;
-
-    int buffer_size_out_overridden;
-    int period_size_out_overridden;
-    int verbose;
-} conf = {
-    .buffer_size_out = 4096,
-    .period_size_out = 1024,
-    .pcm_name_out = "default",
-    .pcm_name_in = "default",
-};
-
 struct alsa_params_req {
     int freq;
     snd_pcm_format_t fmt;
@@ -205,9 +201,7 @@ static void alsa_poll_handler (void *opaque)
     }
 
     if (!(revents & hlp->mask)) {
-        if (conf.verbose) {
-            dolog ("revents = %d\n", revents);
-        }
+        trace_alsa_revents(revents);
         return;
     }
 
@@ -269,15 +263,10 @@ static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask)
             qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, NULL, hlp);
         }
         if (pfds[i].events & POLLOUT) {
-            if (conf.verbose) {
-                dolog ("POLLOUT %d %d\n", i, pfds[i].fd);
-            }
+            trace_alsa_pollout(i, pfds[i].fd);
             qemu_set_fd_handler (pfds[i].fd, NULL, alsa_poll_handler, hlp);
         }
-        if (conf.verbose) {
-            dolog ("Set handler events=%#x index=%d fd=%d err=%d\n",
-                   pfds[i].events, i, pfds[i].fd, err);
-        }
+        trace_alsa_set_handler(pfds[i].events, i, pfds[i].fd, err);
 
     }
     hlp->pfds = pfds;
@@ -464,14 +453,15 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
 }
 
 static int alsa_open (int in, struct alsa_params_req *req,
-                      struct alsa_params_obt *obt, snd_pcm_t **handlep)
+                      struct alsa_params_obt *obt, snd_pcm_t **handlep,
+                      ALSAConf *conf)
 {
     snd_pcm_t *handle;
     snd_pcm_hw_params_t *hw_params;
     int err;
     int size_in_usec;
     unsigned int freq, nchannels;
-    const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;
+    const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out;
     snd_pcm_uframes_t obt_buffer_size;
     const char *typ = in ? "ADC" : "DAC";
     snd_pcm_format_t obtfmt;
@@ -510,7 +500,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
     }
 
     err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
-    if (err < 0 && conf.verbose) {
+    if (err < 0) {
         alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
     }
 
@@ -642,7 +632,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
         goto err;
     }
 
-    if (!in && conf.threshold) {
+    if (!in && conf->threshold) {
         snd_pcm_uframes_t threshold;
         int bytes_per_sec;
 
@@ -664,7 +654,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
             break;
         }
 
-        threshold = (conf.threshold * bytes_per_sec) / 1000;
+        threshold = (conf->threshold * bytes_per_sec) / 1000;
         alsa_set_threshold (handle, threshold);
     }
 
@@ -674,10 +664,9 @@ static int alsa_open (int in, struct alsa_params_req *req,
 
     *handlep = handle;
 
-    if (conf.verbose &&
-        (obtfmt != req->fmt ||
+    if (obtfmt != req->fmt ||
          obt->nchannels != req->nchannels ||
-         obt->freq != req->freq)) {
+         obt->freq != req->freq) {
         dolog ("Audio parameters for %s\n", typ);
         alsa_dump_info (req, obt, obtfmt);
     }
@@ -731,9 +720,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)
             if (written <= 0) {
                 switch (written) {
                 case 0:
-                    if (conf.verbose) {
-                        dolog ("Failed to write %d frames (wrote zero)\n", len);
-                    }
+                    trace_alsa_wrote_zero(len);
                     return;
 
                 case -EPIPE:
@@ -742,9 +729,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)
                                      len);
                         return;
                     }
-                    if (conf.verbose) {
-                        dolog ("Recovering from playback xrun\n");
-                    }
+                    trace_alsa_xrun_out();
                     continue;
 
                 case -ESTRPIPE:
@@ -755,9 +740,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)
                                      len);
                         return;
                     }
-                    if (conf.verbose) {
-                        dolog ("Resuming suspended output stream\n");
-                    }
+                    trace_alsa_resume_out();
                     continue;
 
                 case -EAGAIN:
@@ -807,25 +790,27 @@ static void alsa_fini_out (HWVoiceOut *hw)
     alsa->pcm_buf = NULL;
 }
 
-static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
+                         void *drv_opaque)
 {
     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
     struct alsa_params_req req;
     struct alsa_params_obt obt;
     snd_pcm_t *handle;
     struct audsettings obt_as;
+    ALSAConf *conf = drv_opaque;
 
     req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
-    req.period_size = conf.period_size_out;
-    req.buffer_size = conf.buffer_size_out;
-    req.size_in_usec = conf.size_in_usec_out;
+    req.period_size = conf->period_size_out;
+    req.buffer_size = conf->buffer_size_out;
+    req.size_in_usec = conf->size_in_usec_out;
     req.override_mask =
-        (conf.period_size_out_overridden ? 1 : 0) |
-        (conf.buffer_size_out_overridden ? 2 : 0);
+        (conf->period_size_out_overridden ? 1 : 0) |
+        (conf->buffer_size_out_overridden ? 2 : 0);
 
-    if (alsa_open (0, &req, &obt, &handle)) {
+    if (alsa_open (0, &req, &obt, &handle, conf)) {
         return -1;
     }
 
@@ -846,6 +831,7 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
     }
 
     alsa->handle = handle;
+    alsa->pollhlp.conf = conf;
     return 0;
 }
 
@@ -916,25 +902,26 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return -1;
 }
 
-static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as)
+static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 {
     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
     struct alsa_params_req req;
     struct alsa_params_obt obt;
     snd_pcm_t *handle;
     struct audsettings obt_as;
+    ALSAConf *conf = drv_opaque;
 
     req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
-    req.period_size = conf.period_size_in;
-    req.buffer_size = conf.buffer_size_in;
-    req.size_in_usec = conf.size_in_usec_in;
+    req.period_size = conf->period_size_in;
+    req.buffer_size = conf->buffer_size_in;
+    req.size_in_usec = conf->size_in_usec_in;
     req.override_mask =
-        (conf.period_size_in_overridden ? 1 : 0) |
-        (conf.buffer_size_in_overridden ? 2 : 0);
+        (conf->period_size_in_overridden ? 1 : 0) |
+        (conf->buffer_size_in_overridden ? 2 : 0);
 
-    if (alsa_open (1, &req, &obt, &handle)) {
+    if (alsa_open (1, &req, &obt, &handle, conf)) {
         return -1;
     }
 
@@ -955,6 +942,7 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as)
     }
 
     alsa->handle = handle;
+    alsa->pollhlp.conf = conf;
     return 0;
 }
 
@@ -1010,14 +998,10 @@ static int alsa_run_in (HWVoiceIn *hw)
                 dolog ("Failed to resume suspended input stream\n");
                 return 0;
             }
-            if (conf.verbose) {
-                dolog ("Resuming suspended input stream\n");
-            }
+            trace_alsa_resume_in();
             break;
         default:
-            if (conf.verbose) {
-                dolog ("No frames available and ALSA state is %d\n", state);
-            }
+            trace_alsa_no_frames(state);
             return 0;
         }
     }
@@ -1052,9 +1036,7 @@ static int alsa_run_in (HWVoiceIn *hw)
             if (nread <= 0) {
                 switch (nread) {
                 case 0:
-                    if (conf.verbose) {
-                        dolog ("Failed to read %ld frames (read zero)\n", len);
-                    }
+                    trace_alsa_read_zero(len);
                     goto exit;
 
                 case -EPIPE:
@@ -1062,9 +1044,7 @@ static int alsa_run_in (HWVoiceIn *hw)
                         alsa_logerr (nread, "Failed to read %ld frames\n", len);
                         goto exit;
                     }
-                    if (conf.verbose) {
-                        dolog ("Recovering from capture xrun\n");
-                    }
+                    trace_alsa_xrun_in();
                     continue;
 
                 case -EAGAIN:
@@ -1136,82 +1116,85 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
     return -1;
 }
 
+static ALSAConf glob_conf = {
+    .buffer_size_out = 4096,
+    .period_size_out = 1024,
+    .pcm_name_out = "default",
+    .pcm_name_in = "default",
+};
+
 static void *alsa_audio_init (void)
 {
-    return &conf;
+    ALSAConf *conf = g_malloc(sizeof(ALSAConf));
+    *conf = glob_conf;
+    return conf;
 }
 
 static void alsa_audio_fini (void *opaque)
 {
-    (void) opaque;
+    g_free(opaque);
 }
 
 static struct audio_option alsa_options[] = {
     {
         .name        = "DAC_SIZE_IN_USEC",
         .tag         = AUD_OPT_BOOL,
-        .valp        = &conf.size_in_usec_out,
+        .valp        = &glob_conf.size_in_usec_out,
         .descr       = "DAC period/buffer size in microseconds (otherwise in frames)"
     },
     {
         .name        = "DAC_PERIOD_SIZE",
         .tag         = AUD_OPT_INT,
-        .valp        = &conf.period_size_out,
+        .valp        = &glob_conf.period_size_out,
         .descr       = "DAC period size (0 to go with system default)",
-        .overriddenp = &conf.period_size_out_overridden
+        .overriddenp = &glob_conf.period_size_out_overridden
     },
     {
         .name        = "DAC_BUFFER_SIZE",
         .tag         = AUD_OPT_INT,
-        .valp        = &conf.buffer_size_out,
+        .valp        = &glob_conf.buffer_size_out,
         .descr       = "DAC buffer size (0 to go with system default)",
-        .overriddenp = &conf.buffer_size_out_overridden
+        .overriddenp = &glob_conf.buffer_size_out_overridden
     },
     {
         .name        = "ADC_SIZE_IN_USEC",
         .tag         = AUD_OPT_BOOL,
-        .valp        = &conf.size_in_usec_in,
+        .valp        = &glob_conf.size_in_usec_in,
         .descr       =
         "ADC period/buffer size in microseconds (otherwise in frames)"
     },
     {
         .name        = "ADC_PERIOD_SIZE",
         .tag         = AUD_OPT_INT,
-        .valp        = &conf.period_size_in,
+        .valp        = &glob_conf.period_size_in,
         .descr       = "ADC period size (0 to go with system default)",
-        .overriddenp = &conf.period_size_in_overridden
+        .overriddenp = &glob_conf.period_size_in_overridden
     },
     {
         .name        = "ADC_BUFFER_SIZE",
         .tag         = AUD_OPT_INT,
-        .valp        = &conf.buffer_size_in,
+        .valp        = &glob_conf.buffer_size_in,
         .descr       = "ADC buffer size (0 to go with system default)",
-        .overriddenp = &conf.buffer_size_in_overridden
+        .overriddenp = &glob_conf.buffer_size_in_overridden
     },
     {
         .name        = "THRESHOLD",
         .tag         = AUD_OPT_INT,
-        .valp        = &conf.threshold,
+        .valp        = &glob_conf.threshold,
         .descr       = "(undocumented)"
     },
     {
         .name        = "DAC_DEV",
         .tag         = AUD_OPT_STR,
-        .valp        = &conf.pcm_name_out,
+        .valp        = &glob_conf.pcm_name_out,
         .descr       = "DAC device name (for instance dmix)"
     },
     {
         .name        = "ADC_DEV",
         .tag         = AUD_OPT_STR,
-        .valp        = &conf.pcm_name_in,
+        .valp        = &glob_conf.pcm_name_in,
         .descr       = "ADC device name"
     },
-    {
-        .name        = "VERBOSE",
-        .tag         = AUD_OPT_BOOL,
-        .valp        = &conf.verbose,
-        .descr       = "Behave in a more verbose way"
-    },
     { /* End of list */ }
 };
 
diff --git a/audio/audio.c b/audio/audio.c
index 9d018e9ded..5be4b15fcf 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -30,7 +30,6 @@
 #define AUDIO_CAP "audio"
 #include "audio_int.h"
 
-/* #define DEBUG_PLIVE */
 /* #define DEBUG_LIVE */
 /* #define DEBUG_OUT */
 /* #define DEBUG_CAPTURE */
@@ -66,8 +65,6 @@ static struct {
         int hertz;
         int64_t ticks;
     } period;
-    int plive;
-    int log_to_monitor;
     int try_poll_in;
     int try_poll_out;
 } conf = {
@@ -96,8 +93,6 @@ static struct {
     },
 
     .period = { .hertz = 100 },
-    .plive = 0,
-    .log_to_monitor = 0,
     .try_poll_in = 1,
     .try_poll_out = 1,
 };
@@ -331,20 +326,11 @@ static const char *audio_get_conf_str (const char *key,
 
 void AUD_vlog (const char *cap, const char *fmt, va_list ap)
 {
-    if (conf.log_to_monitor) {
-        if (cap) {
-            monitor_printf(default_mon, "%s: ", cap);
-        }
-
-        monitor_vprintf(default_mon, fmt, ap);
+    if (cap) {
+        fprintf(stderr, "%s: ", cap);
     }
-    else {
-        if (cap) {
-            fprintf (stderr, "%s: ", cap);
-        }
 
-        vfprintf (stderr, fmt, ap);
-    }
+    vfprintf(stderr, fmt, ap);
 }
 
 void AUD_log (const char *cap, const char *fmt, ...)
@@ -1454,9 +1440,6 @@ static void audio_run_out (AudioState *s)
             while (sw) {
                 sw1 = sw->entries.le_next;
                 if (!sw->active && !sw->callback.fn) {
-#ifdef DEBUG_PLIVE
-                    dolog ("Finishing with old voice\n");
-#endif
                     audio_close_out (sw);
                 }
                 sw = sw1;
@@ -1648,18 +1631,6 @@ static struct audio_option audio_options[] = {
         .valp  = &conf.period.hertz,
         .descr = "Timer period in HZ (0 - use lowest possible)"
     },
-    {
-        .name  = "PLIVE",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.plive,
-        .descr = "(undocumented)"
-    },
-    {
-        .name  = "LOG_TO_MONITOR",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.log_to_monitor,
-        .descr = "Print logging messages to monitor instead of stderr"
-    },
     { /* End of list */ }
 };
 
diff --git a/audio/audio_int.h b/audio/audio_int.h
index fd019a0fc3..566df5edf4 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -156,13 +156,13 @@ struct audio_driver {
 };
 
 struct audio_pcm_ops {
-    int  (*init_out)(HWVoiceOut *hw, struct audsettings *as);
+    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, struct audsettings *as);
+    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);
@@ -206,14 +206,11 @@ extern struct audio_driver no_audio_driver;
 extern struct audio_driver oss_audio_driver;
 extern struct audio_driver sdl_audio_driver;
 extern struct audio_driver wav_audio_driver;
-extern struct audio_driver fmod_audio_driver;
 extern struct audio_driver alsa_audio_driver;
 extern struct audio_driver coreaudio_audio_driver;
 extern struct audio_driver dsound_audio_driver;
-extern struct audio_driver esd_audio_driver;
 extern struct audio_driver pa_audio_driver;
 extern struct audio_driver spice_audio_driver;
-extern struct audio_driver winwave_audio_driver;
 extern const struct mixeng_volume nominal_volume;
 
 void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 584e536fac..99b27b285e 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -262,7 +262,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
 #ifdef DAC
     QLIST_INIT (&hw->cap_head);
 #endif
-    if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
+    if (glue (hw->pcm_ops->init_, TYPE) (hw, as, s->drv_opaque)) {
         goto err0;
     }
 
@@ -398,10 +398,6 @@ SW *glue (AUD_open_, TYPE) (
     )
 {
     AudioState *s = &glob_audio_state;
-#ifdef DAC
-    int live = 0;
-    SW *old_sw = NULL;
-#endif
 
     if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) {
         dolog ("card=%p name=%p callback_fn=%p as=%p\n",
@@ -426,29 +422,6 @@ SW *glue (AUD_open_, TYPE) (
         return sw;
     }
 
-#ifdef DAC
-    if (conf.plive && sw && (!sw->active && !sw->empty)) {
-        live = sw->total_hw_samples_mixed;
-
-#ifdef DEBUG_PLIVE
-        dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live);
-        dolog ("Old %s freq %d, bits %d, channels %d\n",
-               SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels);
-        dolog ("New %s freq %d, bits %d, channels %d\n",
-               name,
-               as->freq,
-               (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) ? 16 : 8,
-               as->nchannels);
-#endif
-
-        if (live) {
-            old_sw = sw;
-            old_sw->callback.fn = NULL;
-            sw = NULL;
-        }
-    }
-#endif
-
     if (!glue (conf.fixed_, TYPE).enabled && sw) {
         glue (AUD_close_, TYPE) (card, sw);
         sw = NULL;
@@ -481,20 +454,6 @@ SW *glue (AUD_open_, TYPE) (
     sw->callback.fn = callback_fn;
     sw->callback.opaque = callback_opaque;
 
-#ifdef DAC
-    if (live) {
-        int mixed =
-            (live << old_sw->info.shift)
-            * old_sw->info.bytes_per_second
-            / sw->info.bytes_per_second;
-
-#ifdef DEBUG_PLIVE
-        dolog ("Silence will be mixed %d\n", mixed);
-#endif
-        sw->total_hw_samples_mixed += mixed;
-    }
-#endif
-
 #ifdef DEBUG_AUDIO
     dolog ("%s\n", name);
     audio_pcm_print_info ("hw", &sw->hw->info);
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index 5964c62eaf..6dfd63eb42 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -32,20 +32,16 @@
 #define AUDIO_CAP "coreaudio"
 #include "audio_int.h"
 
-struct {
+static int isAtexit;
+
+typedef struct {
     int buffer_frames;
     int nbuffers;
-    int isAtexit;
-} conf = {
-    .buffer_frames = 512,
-    .nbuffers = 4,
-    .isAtexit = 0
-};
+} CoreaudioConf;
 
 typedef struct coreaudioVoiceOut {
     HWVoiceOut hw;
     pthread_mutex_t mutex;
-    int isAtexit;
     AudioDeviceID outputDeviceID;
     UInt32 audioDevicePropertyBufferFrameSize;
     AudioStreamBasicDescription outputStreamBasicDescription;
@@ -161,7 +157,7 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
 
 static void coreaudio_atexit (void)
 {
-    conf.isAtexit = 1;
+    isAtexit = 1;
 }
 
 static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
@@ -287,7 +283,8 @@ 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)
+static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
+                              void *drv_opaque)
 {
     OSStatus status;
     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
@@ -295,6 +292,7 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
     int err;
     const char *typ = "playback";
     AudioValueRange frameRange;
+    CoreaudioConf *conf = drv_opaque;
 
     /* create mutex */
     err = pthread_mutex_init(&core->mutex, NULL);
@@ -336,16 +334,16 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
         return -1;
     }
 
-    if (frameRange.mMinimum > conf.buffer_frames) {
+    if (frameRange.mMinimum > conf->buffer_frames) {
         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
         dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
     }
-    else if (frameRange.mMaximum < conf.buffer_frames) {
+    else if (frameRange.mMaximum < conf->buffer_frames) {
         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
         dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
     }
     else {
-        core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
+        core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
     }
 
     /* set Buffer Frame Size */
@@ -379,7 +377,7 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
                            "Could not get device buffer frame size\n");
         return -1;
     }
-    hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
+    hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
 
     /* get StreamFormat */
     propertySize = sizeof(core->outputStreamBasicDescription);
@@ -443,7 +441,7 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
     int err;
     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 
-    if (!conf.isAtexit) {
+    if (!isAtexit) {
         /* stop playback */
         if (isPlaying(core->outputDeviceID)) {
             status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
@@ -486,7 +484,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
 
     case VOICE_DISABLE:
         /* stop playback */
-        if (!conf.isAtexit) {
+        if (!isAtexit) {
             if (isPlaying(core->outputDeviceID)) {
                 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
                 if (status != kAudioHardwareNoError) {
@@ -499,28 +497,36 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return 0;
 }
 
+static CoreaudioConf glob_conf = {
+    .buffer_frames = 512,
+    .nbuffers = 4,
+};
+
 static void *coreaudio_audio_init (void)
 {
+    CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
+    *conf = glob_conf;
+
     atexit(coreaudio_atexit);
-    return &coreaudio_audio_init;
+    return conf;
 }
 
 static void coreaudio_audio_fini (void *opaque)
 {
-    (void) opaque;
+    g_free(opaque);
 }
 
 static struct audio_option coreaudio_options[] = {
     {
         .name  = "BUFFER_SIZE",
         .tag   = AUD_OPT_INT,
-        .valp  = &conf.buffer_frames,
+        .valp  = &glob_conf.buffer_frames,
         .descr = "Size of the buffer in frames"
     },
     {
         .name  = "BUFFER_COUNT",
         .tag   = AUD_OPT_INT,
-        .valp  = &conf.nbuffers,
+        .valp  = &glob_conf.nbuffers,
         .descr = "Number of buffers"
     },
     { /* End of list */ }
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
index 8b37d16a8c..b439f33f58 100644
--- a/audio/dsound_template.h
+++ b/audio/dsound_template.h
@@ -67,11 +67,11 @@ static int glue (dsound_lock_, TYPE) (
     LPVOID *p2p,
     DWORD *blen1p,
     DWORD *blen2p,
-    int entire
+    int entire,
+    dsound *s
     )
 {
     HRESULT hr;
-    int i;
     LPVOID p1 = NULL, p2 = NULL;
     DWORD blen1 = 0, blen2 = 0;
     DWORD flag;
@@ -81,37 +81,18 @@ static int glue (dsound_lock_, TYPE) (
 #else
     flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
 #endif
-    for (i = 0; i < conf.lock_retries; ++i) {
-        hr = glue (IFACE, _Lock) (
-            buf,
-            pos,
-            len,
-            &p1,
-            &blen1,
-            &p2,
-            &blen2,
-            flag
-            );
+    hr = glue(IFACE, _Lock)(buf, pos, len, &p1, &blen1, &p2, &blen2, flag);
 
-        if (FAILED (hr)) {
+    if (FAILED (hr)) {
 #ifndef DSBTYPE_IN
-            if (hr == DSERR_BUFFERLOST) {
-                if (glue (dsound_restore_, TYPE) (buf)) {
-                    dsound_logerr (hr, "Could not lock " NAME "\n");
-                    goto fail;
-                }
-                continue;
+        if (hr == DSERR_BUFFERLOST) {
+            if (glue (dsound_restore_, TYPE) (buf, s)) {
+                dsound_logerr (hr, "Could not lock " NAME "\n");
             }
-#endif
-            dsound_logerr (hr, "Could not lock " NAME "\n");
             goto fail;
         }
-
-        break;
-    }
-
-    if (i == conf.lock_retries) {
-        dolog ("%d attempts to lock " NAME " failed\n", i);
+#endif
+        dsound_logerr (hr, "Could not lock " NAME "\n");
         goto fail;
     }
 
@@ -174,16 +155,19 @@ static void dsound_fini_out (HWVoiceOut *hw)
 }
 
 #ifdef DSBTYPE_IN
-static int dsound_init_in (HWVoiceIn *hw, struct audsettings *as)
+static int dsound_init_in(HWVoiceIn *hw, struct audsettings *as,
+                          void *drv_opaque)
 #else
-static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
+                           void *drv_opaque)
 #endif
 {
     int err;
     HRESULT hr;
-    dsound *s = &glob_dsound;
+    dsound *s = drv_opaque;
     WAVEFORMATEX wfx;
     struct audsettings obt_as;
+    DSoundConf *conf = &s->conf;
 #ifdef DSBTYPE_IN
     const char *typ = "ADC";
     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
@@ -210,7 +194,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
     bd.dwSize = sizeof (bd);
     bd.lpwfxFormat = &wfx;
 #ifdef DSBTYPE_IN
-    bd.dwBufferBytes = conf.bufsize_in;
+    bd.dwBufferBytes = conf->bufsize_in;
     hr = IDirectSoundCapture_CreateCaptureBuffer (
         s->dsound_capture,
         &bd,
@@ -219,7 +203,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
         );
 #else
     bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
-    bd.dwBufferBytes = conf.bufsize_out;
+    bd.dwBufferBytes = conf->bufsize_out;
     hr = IDirectSound_CreateSoundBuffer (
         s->dsound,
         &bd,
@@ -269,6 +253,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
             );
     }
     hw->samples = bc.dwBufferBytes >> hw->info.shift;
+    ds->s = s;
 
 #ifdef DEBUG_DSOUND
     dolog ("caps %ld, desc %ld\n",
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index e2d89fd5d5..e9472c105c 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -41,42 +41,25 @@
 
 /* #define DEBUG_DSOUND */
 
-static struct {
-    int lock_retries;
-    int restore_retries;
-    int getstatus_retries;
-    int set_primary;
+typedef struct {
     int bufsize_in;
     int bufsize_out;
-    struct audsettings settings;
     int latency_millis;
-} conf = {
-    .lock_retries       = 1,
-    .restore_retries    = 1,
-    .getstatus_retries  = 1,
-    .set_primary        = 0,
-    .bufsize_in         = 16384,
-    .bufsize_out        = 16384,
-    .settings.freq      = 44100,
-    .settings.nchannels = 2,
-    .settings.fmt       = AUD_FMT_S16,
-    .latency_millis     = 10
-};
+} DSoundConf;
 
 typedef struct {
     LPDIRECTSOUND dsound;
     LPDIRECTSOUNDCAPTURE dsound_capture;
-    LPDIRECTSOUNDBUFFER dsound_primary_buffer;
     struct audsettings settings;
+    DSoundConf conf;
 } dsound;
 
-static dsound glob_dsound;
-
 typedef struct {
     HWVoiceOut hw;
     LPDIRECTSOUNDBUFFER dsound_buffer;
     DWORD old_pos;
     int first_time;
+    dsound *s;
 #ifdef DEBUG_DSOUND
     DWORD old_ppos;
     DWORD played;
@@ -88,6 +71,7 @@ typedef struct {
     HWVoiceIn hw;
     int first_time;
     LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
+    dsound *s;
 } DSoundVoiceIn;
 
 static void dsound_log_hresult (HRESULT hr)
@@ -281,29 +265,17 @@ static void print_wave_format (WAVEFORMATEX *wfx)
 }
 #endif
 
-static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
+static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s)
 {
     HRESULT hr;
-    int i;
-
-    for (i = 0; i < conf.restore_retries; ++i) {
-        hr = IDirectSoundBuffer_Restore (dsb);
-
-        switch (hr) {
-        case DS_OK:
-            return 0;
 
-        case DSERR_BUFFERLOST:
-            continue;
+    hr = IDirectSoundBuffer_Restore (dsb);
 
-        default:
-            dsound_logerr (hr, "Could not restore playback buffer\n");
-            return -1;
-        }
+    if (hr != DS_OK) {
+        dsound_logerr (hr, "Could not restore playback buffer\n");
+        return -1;
     }
-
-    dolog ("%d attempts to restore playback buffer failed\n", i);
-    return -1;
+    return 0;
 }
 
 #include "dsound_template.h"
@@ -311,25 +283,20 @@ static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
 #include "dsound_template.h"
 #undef DSBTYPE_IN
 
-static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp)
+static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
+                                  dsound *s)
 {
     HRESULT hr;
-    int i;
 
-    for (i = 0; i < conf.getstatus_retries; ++i) {
-        hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
-        if (FAILED (hr)) {
-            dsound_logerr (hr, "Could not get playback buffer status\n");
-            return -1;
-        }
+    hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not get playback buffer status\n");
+        return -1;
+    }
 
-        if (*statusp & DSERR_BUFFERLOST) {
-            if (dsound_restore_out (dsb)) {
-                return -1;
-            }
-            continue;
-        }
-        break;
+    if (*statusp & DSERR_BUFFERLOST) {
+        dsound_restore_out(dsb, s);
+        return -1;
     }
 
     return 0;
@@ -376,7 +343,8 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
     hw->rpos = pos % hw->samples;
 }
 
-static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
+static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
+                                 dsound *s)
 {
     int err;
     LPVOID p1, p2;
@@ -389,7 +357,8 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
         hw->samples << hw->info.shift,
         &p1, &p2,
         &blen1, &blen2,
-        1
+        1,
+        s
         );
     if (err) {
         return;
@@ -415,25 +384,9 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
     dsound_unlock_out (dsb, p1, p2, blen1, blen2);
 }
 
-static void dsound_close (dsound *s)
-{
-    HRESULT hr;
-
-    if (s->dsound_primary_buffer) {
-        hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
-        if (FAILED (hr)) {
-            dsound_logerr (hr, "Could not release primary buffer\n");
-        }
-        s->dsound_primary_buffer = NULL;
-    }
-}
-
 static int dsound_open (dsound *s)
 {
-    int err;
     HRESULT hr;
-    WAVEFORMATEX wfx;
-    DSBUFFERDESC dsbd;
     HWND hwnd;
 
     hwnd = GetForegroundWindow ();
@@ -449,63 +402,7 @@ static int dsound_open (dsound *s)
         return -1;
     }
 
-    if (!conf.set_primary) {
-        return 0;
-    }
-
-    err = waveformat_from_audio_settings (&wfx, &conf.settings);
-    if (err) {
-        return -1;
-    }
-
-    memset (&dsbd, 0, sizeof (dsbd));
-    dsbd.dwSize = sizeof (dsbd);
-    dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
-    dsbd.dwBufferBytes = 0;
-    dsbd.lpwfxFormat = NULL;
-
-    hr = IDirectSound_CreateSoundBuffer (
-        s->dsound,
-        &dsbd,
-        &s->dsound_primary_buffer,
-        NULL
-        );
-    if (FAILED (hr)) {
-        dsound_logerr (hr, "Could not create primary playback buffer\n");
-        return -1;
-    }
-
-    hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
-    if (FAILED (hr)) {
-        dsound_logerr (hr, "Could not set primary playback buffer format\n");
-    }
-
-    hr = IDirectSoundBuffer_GetFormat (
-        s->dsound_primary_buffer,
-        &wfx,
-        sizeof (wfx),
-        NULL
-        );
-    if (FAILED (hr)) {
-        dsound_logerr (hr, "Could not get primary playback buffer format\n");
-        goto fail0;
-    }
-
-#ifdef DEBUG_DSOUND
-    dolog ("Primary\n");
-    print_wave_format (&wfx);
-#endif
-
-    err = waveformat_to_audio_settings (&wfx, &s->settings);
-    if (err) {
-        goto fail0;
-    }
-
     return 0;
-
- fail0:
-    dsound_close (s);
-    return -1;
 }
 
 static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
@@ -514,6 +411,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
     DWORD status;
     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
+    dsound *s = ds->s;
 
     if (!dsb) {
         dolog ("Attempt to control voice without a buffer\n");
@@ -522,7 +420,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
 
     switch (cmd) {
     case VOICE_ENABLE:
-        if (dsound_get_status_out (dsb, &status)) {
+        if (dsound_get_status_out (dsb, &status, s)) {
             return -1;
         }
 
@@ -531,7 +429,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
             return 0;
         }
 
-        dsound_clear_sample (hw, dsb);
+        dsound_clear_sample (hw, dsb, s);
 
         hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
         if (FAILED (hr)) {
@@ -541,7 +439,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
         break;
 
     case VOICE_DISABLE:
-        if (dsound_get_status_out (dsb, &status)) {
+        if (dsound_get_status_out (dsb, &status, s)) {
             return -1;
         }
 
@@ -578,6 +476,8 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
     DWORD wpos, ppos, old_pos;
     LPVOID p1, p2;
     int bufsize;
+    dsound *s = ds->s;
+    DSoundConf *conf = &s->conf;
 
     if (!dsb) {
         dolog ("Attempt to run empty with playback buffer\n");
@@ -600,14 +500,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
     len = live << hwshift;
 
     if (ds->first_time) {
-        if (conf.latency_millis) {
+        if (conf->latency_millis) {
             DWORD cur_blat;
 
             cur_blat = audio_ring_dist (wpos, ppos, bufsize);
             ds->first_time = 0;
             old_pos = wpos;
             old_pos +=
-                millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat;
+                millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat;
             old_pos %= bufsize;
             old_pos &= ~hw->info.align;
         }
@@ -663,7 +563,8 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
         len,
         &p1, &p2,
         &blen1, &blen2,
-        0
+        0,
+        s
         );
     if (err) {
         return 0;
@@ -766,6 +667,7 @@ static int dsound_run_in (HWVoiceIn *hw)
     DWORD cpos, rpos;
     LPVOID p1, p2;
     int hwshift;
+    dsound *s = ds->s;
 
     if (!dscb) {
         dolog ("Attempt to run without capture buffer\n");
@@ -820,7 +722,8 @@ static int dsound_run_in (HWVoiceIn *hw)
         &p2,
         &blen1,
         &blen2,
-        0
+        0,
+        s
         );
     if (err) {
         return 0;
@@ -843,12 +746,19 @@ static int dsound_run_in (HWVoiceIn *hw)
     return decr;
 }
 
+static DSoundConf glob_conf = {
+    .bufsize_in         = 16384,
+    .bufsize_out        = 16384,
+    .latency_millis     = 10
+};
+
 static void dsound_audio_fini (void *opaque)
 {
     HRESULT hr;
     dsound *s = opaque;
 
     if (!s->dsound) {
+        g_free(s);
         return;
     }
 
@@ -859,6 +769,7 @@ static void dsound_audio_fini (void *opaque)
     s->dsound = NULL;
 
     if (!s->dsound_capture) {
+        g_free(s);
         return;
     }
 
@@ -867,17 +778,21 @@ static void dsound_audio_fini (void *opaque)
         dsound_logerr (hr, "Could not release DirectSoundCapture\n");
     }
     s->dsound_capture = NULL;
+
+    g_free(s);
 }
 
 static void *dsound_audio_init (void)
 {
     int err;
     HRESULT hr;
-    dsound *s = &glob_dsound;
+    dsound *s = g_malloc0(sizeof(dsound));
 
+    s->conf = glob_conf;
     hr = CoInitialize (NULL);
     if (FAILED (hr)) {
         dsound_logerr (hr, "Could not initialize COM\n");
+        g_free(s);
         return NULL;
     }
 
@@ -890,6 +805,7 @@ static void *dsound_audio_init (void)
         );
     if (FAILED (hr)) {
         dsound_logerr (hr, "Could not create DirectSound instance\n");
+        g_free(s);
         return NULL;
     }
 
@@ -901,7 +817,7 @@ static void *dsound_audio_init (void)
         if (FAILED (hr)) {
             dsound_logerr (hr, "Could not release DirectSound\n");
         }
-        s->dsound = NULL;
+        g_free(s);
         return NULL;
     }
 
@@ -939,63 +855,21 @@ static void *dsound_audio_init (void)
 
 static struct audio_option dsound_options[] = {
     {
-        .name  = "LOCK_RETRIES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.lock_retries,
-        .descr = "Number of times to attempt locking the buffer"
-    },
-    {
-        .name  = "RESTOURE_RETRIES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.restore_retries,
-        .descr = "Number of times to attempt restoring the buffer"
-    },
-    {
-        .name  = "GETSTATUS_RETRIES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.getstatus_retries,
-        .descr = "Number of times to attempt getting status of the buffer"
-    },
-    {
-        .name  = "SET_PRIMARY",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.set_primary,
-        .descr = "Set the parameters of primary buffer"
-    },
-    {
         .name  = "LATENCY_MILLIS",
         .tag   = AUD_OPT_INT,
-        .valp  = &conf.latency_millis,
+        .valp  = &glob_conf.latency_millis,
         .descr = "(undocumented)"
     },
     {
-        .name  = "PRIMARY_FREQ",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.settings.freq,
-        .descr = "Primary buffer frequency"
-    },
-    {
-        .name  = "PRIMARY_CHANNELS",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.settings.nchannels,
-        .descr = "Primary buffer number of channels (1 - mono, 2 - stereo)"
-    },
-    {
-        .name  = "PRIMARY_FMT",
-        .tag   = AUD_OPT_FMT,
-        .valp  = &conf.settings.fmt,
-        .descr = "Primary buffer format"
-    },
-    {
         .name  = "BUFSIZE_OUT",
         .tag   = AUD_OPT_INT,
-        .valp  = &conf.bufsize_out,
+        .valp  = &glob_conf.bufsize_out,
         .descr = "(undocumented)"
     },
     {
         .name  = "BUFSIZE_IN",
         .tag   = AUD_OPT_INT,
-        .valp  = &conf.bufsize_in,
+        .valp  = &glob_conf.bufsize_in,
         .descr = "(undocumented)"
     },
     { /* End of list */ }
diff --git a/audio/esdaudio.c b/audio/esdaudio.c
deleted file mode 100644
index eea9ccec0b..0000000000
--- a/audio/esdaudio.c
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * QEMU ESD audio driver
- *
- * Copyright (c) 2006 Frederick Reeve (brushed up by malc)
- *
- * 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 <esd.h>
-#include "qemu-common.h"
-#include "audio.h"
-
-#define AUDIO_CAP "esd"
-#include "audio_int.h"
-#include "audio_pt_int.h"
-
-typedef struct {
-    HWVoiceOut hw;
-    int done;
-    int live;
-    int decr;
-    int rpos;
-    void *pcm_buf;
-    int fd;
-    struct audio_pt pt;
-} ESDVoiceOut;
-
-typedef struct {
-    HWVoiceIn hw;
-    int done;
-    int dead;
-    int incr;
-    int wpos;
-    void *pcm_buf;
-    int fd;
-    struct audio_pt pt;
-} ESDVoiceIn;
-
-static struct {
-    int samples;
-    int divisor;
-    char *dac_host;
-    char *adc_host;
-} conf = {
-    .samples = 1024,
-    .divisor = 2,
-};
-
-static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
-{
-    va_list ap;
-
-    va_start (ap, fmt);
-    AUD_vlog (AUDIO_CAP, fmt, ap);
-    va_end (ap);
-
-    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
-}
-
-/* playback */
-static void *qesd_thread_out (void *arg)
-{
-    ESDVoiceOut *esd = arg;
-    HWVoiceOut *hw = &esd->hw;
-    int threshold;
-
-    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
-
-    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
-        return NULL;
-    }
-
-    for (;;) {
-        int decr, to_mix, rpos;
-
-        for (;;) {
-            if (esd->done) {
-                goto exit;
-            }
-
-            if (esd->live > threshold) {
-                break;
-            }
-
-            if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
-                goto exit;
-            }
-        }
-
-        decr = to_mix = esd->live;
-        rpos = hw->rpos;
-
-        if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
-            return NULL;
-        }
-
-        while (to_mix) {
-            ssize_t written;
-            int chunk = audio_MIN (to_mix, hw->samples - rpos);
-            struct st_sample *src = hw->mix_buf + rpos;
-
-            hw->clip (esd->pcm_buf, src, chunk);
-
-        again:
-            written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
-            if (written == -1) {
-                if (errno == EINTR || errno == EAGAIN) {
-                    goto again;
-                }
-                qesd_logerr (errno, "write failed\n");
-                return NULL;
-            }
-
-            if (written != chunk << hw->info.shift) {
-                int wsamples = written >> hw->info.shift;
-                int wbytes = wsamples << hw->info.shift;
-                if (wbytes != written) {
-                    dolog ("warning: Misaligned write %d (requested %zd), "
-                           "alignment %d\n",
-                           wbytes, written, hw->info.align + 1);
-                }
-                to_mix -= wsamples;
-                rpos = (rpos + wsamples) % hw->samples;
-                break;
-            }
-
-            rpos = (rpos + chunk) % hw->samples;
-            to_mix -= chunk;
-        }
-
-        if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
-            return NULL;
-        }
-
-        esd->rpos = rpos;
-        esd->live -= decr;
-        esd->decr += decr;
-    }
-
- exit:
-    audio_pt_unlock (&esd->pt, AUDIO_FUNC);
-    return NULL;
-}
-
-static int qesd_run_out (HWVoiceOut *hw, int live)
-{
-    int decr;
-    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
-
-    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
-        return 0;
-    }
-
-    decr = audio_MIN (live, esd->decr);
-    esd->decr -= decr;
-    esd->live = live - decr;
-    hw->rpos = esd->rpos;
-    if (esd->live > 0) {
-        audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
-    }
-    else {
-        audio_pt_unlock (&esd->pt, AUDIO_FUNC);
-    }
-    return decr;
-}
-
-static int qesd_write (SWVoiceOut *sw, void *buf, int len)
-{
-    return audio_pcm_sw_write (sw, buf, len);
-}
-
-static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
-{
-    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
-    struct audsettings obt_as = *as;
-    int esdfmt = ESD_STREAM | ESD_PLAY;
-
-    esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
-    switch (as->fmt) {
-    case AUD_FMT_S8:
-    case AUD_FMT_U8:
-        esdfmt |= ESD_BITS8;
-        obt_as.fmt = AUD_FMT_U8;
-        break;
-
-    case AUD_FMT_S32:
-    case AUD_FMT_U32:
-        dolog ("Will use 16 instead of 32 bit samples\n");
-        /* fall through */
-    case AUD_FMT_S16:
-    case AUD_FMT_U16:
-    deffmt:
-        esdfmt |= ESD_BITS16;
-        obt_as.fmt = AUD_FMT_S16;
-        break;
-
-    default:
-        dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
-        goto deffmt;
-
-    }
-    obt_as.endianness = AUDIO_HOST_ENDIANNESS;
-
-    audio_pcm_init_info (&hw->info, &obt_as);
-
-    hw->samples = conf.samples;
-    esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
-    if (!esd->pcm_buf) {
-        dolog ("Could not allocate buffer (%d bytes)\n",
-               hw->samples << hw->info.shift);
-        return -1;
-    }
-
-    esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
-    if (esd->fd < 0) {
-        qesd_logerr (errno, "esd_play_stream failed\n");
-        goto fail1;
-    }
-
-    if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
-        goto fail2;
-    }
-
-    return 0;
-
- fail2:
-    if (close (esd->fd)) {
-        qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
-                     AUDIO_FUNC, esd->fd);
-    }
-    esd->fd = -1;
-
- fail1:
-    g_free (esd->pcm_buf);
-    esd->pcm_buf = NULL;
-    return -1;
-}
-
-static void qesd_fini_out (HWVoiceOut *hw)
-{
-    void *ret;
-    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
-
-    audio_pt_lock (&esd->pt, AUDIO_FUNC);
-    esd->done = 1;
-    audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
-    audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
-
-    if (esd->fd >= 0) {
-        if (close (esd->fd)) {
-            qesd_logerr (errno, "failed to close esd socket\n");
-        }
-        esd->fd = -1;
-    }
-
-    audio_pt_fini (&esd->pt, AUDIO_FUNC);
-
-    g_free (esd->pcm_buf);
-    esd->pcm_buf = NULL;
-}
-
-static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
-{
-    (void) hw;
-    (void) cmd;
-    return 0;
-}
-
-/* capture */
-static void *qesd_thread_in (void *arg)
-{
-    ESDVoiceIn *esd = arg;
-    HWVoiceIn *hw = &esd->hw;
-    int threshold;
-
-    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
-
-    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
-        return NULL;
-    }
-
-    for (;;) {
-        int incr, to_grab, wpos;
-
-        for (;;) {
-            if (esd->done) {
-                goto exit;
-            }
-
-            if (esd->dead > threshold) {
-                break;
-            }
-
-            if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
-                goto exit;
-            }
-        }
-
-        incr = to_grab = esd->dead;
-        wpos = hw->wpos;
-
-        if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
-            return NULL;
-        }
-
-        while (to_grab) {
-            ssize_t nread;
-            int chunk = audio_MIN (to_grab, hw->samples - wpos);
-            void *buf = advance (esd->pcm_buf, wpos);
-
-        again:
-            nread = read (esd->fd, buf, chunk << hw->info.shift);
-            if (nread == -1) {
-                if (errno == EINTR || errno == EAGAIN) {
-                    goto again;
-                }
-                qesd_logerr (errno, "read failed\n");
-                return NULL;
-            }
-
-            if (nread != chunk << hw->info.shift) {
-                int rsamples = nread >> hw->info.shift;
-                int rbytes = rsamples << hw->info.shift;
-                if (rbytes != nread) {
-                    dolog ("warning: Misaligned write %d (requested %zd), "
-                           "alignment %d\n",
-                           rbytes, nread, hw->info.align + 1);
-                }
-                to_grab -= rsamples;
-                wpos = (wpos + rsamples) % hw->samples;
-                break;
-            }
-
-            hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift);
-            wpos = (wpos + chunk) % hw->samples;
-            to_grab -= chunk;
-        }
-
-        if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
-            return NULL;
-        }
-
-        esd->wpos = wpos;
-        esd->dead -= incr;
-        esd->incr += incr;
-    }
-
- exit:
-    audio_pt_unlock (&esd->pt, AUDIO_FUNC);
-    return NULL;
-}
-
-static int qesd_run_in (HWVoiceIn *hw)
-{
-    int live, incr, dead;
-    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
-
-    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
-        return 0;
-    }
-
-    live = audio_pcm_hw_get_live_in (hw);
-    dead = hw->samples - live;
-    incr = audio_MIN (dead, esd->incr);
-    esd->incr -= incr;
-    esd->dead = dead - incr;
-    hw->wpos = esd->wpos;
-    if (esd->dead > 0) {
-        audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
-    }
-    else {
-        audio_pt_unlock (&esd->pt, AUDIO_FUNC);
-    }
-    return incr;
-}
-
-static int qesd_read (SWVoiceIn *sw, void *buf, int len)
-{
-    return audio_pcm_sw_read (sw, buf, len);
-}
-
-static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
-{
-    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
-    struct audsettings obt_as = *as;
-    int esdfmt = ESD_STREAM | ESD_RECORD;
-
-    esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
-    switch (as->fmt) {
-    case AUD_FMT_S8:
-    case AUD_FMT_U8:
-        esdfmt |= ESD_BITS8;
-        obt_as.fmt = AUD_FMT_U8;
-        break;
-
-    case AUD_FMT_S16:
-    case AUD_FMT_U16:
-        esdfmt |= ESD_BITS16;
-        obt_as.fmt = AUD_FMT_S16;
-        break;
-
-    case AUD_FMT_S32:
-    case AUD_FMT_U32:
-        dolog ("Will use 16 instead of 32 bit samples\n");
-        esdfmt |= ESD_BITS16;
-        obt_as.fmt = AUD_FMT_S16;
-        break;
-    }
-    obt_as.endianness = AUDIO_HOST_ENDIANNESS;
-
-    audio_pcm_init_info (&hw->info, &obt_as);
-
-    hw->samples = conf.samples;
-    esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
-    if (!esd->pcm_buf) {
-        dolog ("Could not allocate buffer (%d bytes)\n",
-               hw->samples << hw->info.shift);
-        return -1;
-    }
-
-    esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
-    if (esd->fd < 0) {
-        qesd_logerr (errno, "esd_record_stream failed\n");
-        goto fail1;
-    }
-
-    if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
-        goto fail2;
-    }
-
-    return 0;
-
- fail2:
-    if (close (esd->fd)) {
-        qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
-                     AUDIO_FUNC, esd->fd);
-    }
-    esd->fd = -1;
-
- fail1:
-    g_free (esd->pcm_buf);
-    esd->pcm_buf = NULL;
-    return -1;
-}
-
-static void qesd_fini_in (HWVoiceIn *hw)
-{
-    void *ret;
-    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
-
-    audio_pt_lock (&esd->pt, AUDIO_FUNC);
-    esd->done = 1;
-    audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
-    audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
-
-    if (esd->fd >= 0) {
-        if (close (esd->fd)) {
-            qesd_logerr (errno, "failed to close esd socket\n");
-        }
-        esd->fd = -1;
-    }
-
-    audio_pt_fini (&esd->pt, AUDIO_FUNC);
-
-    g_free (esd->pcm_buf);
-    esd->pcm_buf = NULL;
-}
-
-static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
-{
-    (void) hw;
-    (void) cmd;
-    return 0;
-}
-
-/* common */
-static void *qesd_audio_init (void)
-{
-    return &conf;
-}
-
-static void qesd_audio_fini (void *opaque)
-{
-    (void) opaque;
-    ldebug ("esd_fini");
-}
-
-struct audio_option qesd_options[] = {
-    {
-        .name  = "SAMPLES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.samples,
-        .descr = "buffer size in samples"
-    },
-    {
-        .name  = "DIVISOR",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.divisor,
-        .descr = "threshold divisor"
-    },
-    {
-        .name  = "DAC_HOST",
-        .tag   = AUD_OPT_STR,
-        .valp  = &conf.dac_host,
-        .descr = "playback host"
-    },
-    {
-        .name  = "ADC_HOST",
-        .tag   = AUD_OPT_STR,
-        .valp  = &conf.adc_host,
-        .descr = "capture host"
-    },
-    { /* End of list */ }
-};
-
-static struct audio_pcm_ops qesd_pcm_ops = {
-    .init_out = qesd_init_out,
-    .fini_out = qesd_fini_out,
-    .run_out  = qesd_run_out,
-    .write    = qesd_write,
-    .ctl_out  = qesd_ctl_out,
-
-    .init_in  = qesd_init_in,
-    .fini_in  = qesd_fini_in,
-    .run_in   = qesd_run_in,
-    .read     = qesd_read,
-    .ctl_in   = qesd_ctl_in,
-};
-
-struct audio_driver esd_audio_driver = {
-    .name           = "esd",
-    .descr          = "http://en.wikipedia.org/wiki/Esound",
-    .options        = qesd_options,
-    .init           = qesd_audio_init,
-    .fini           = qesd_audio_fini,
-    .pcm_ops        = &qesd_pcm_ops,
-    .can_be_default = 0,
-    .max_voices_out = INT_MAX,
-    .max_voices_in  = INT_MAX,
-    .voice_size_out = sizeof (ESDVoiceOut),
-    .voice_size_in  = sizeof (ESDVoiceIn)
-};
diff --git a/audio/fmodaudio.c b/audio/fmodaudio.c
deleted file mode 100644
index fabf84dd3b..0000000000
--- a/audio/fmodaudio.c
+++ /dev/null
@@ -1,685 +0,0 @@
-/*
- * QEMU FMOD audio driver
- *
- * Copyright (c) 2004-2005 Vassili Karpov (malc)
- *
- * 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 <fmod.h>
-#include <fmod_errors.h>
-#include "qemu-common.h"
-#include "audio.h"
-
-#define AUDIO_CAP "fmod"
-#include "audio_int.h"
-
-typedef struct FMODVoiceOut {
-    HWVoiceOut hw;
-    unsigned int old_pos;
-    FSOUND_SAMPLE *fmod_sample;
-    int channel;
-} FMODVoiceOut;
-
-typedef struct FMODVoiceIn {
-    HWVoiceIn hw;
-    FSOUND_SAMPLE *fmod_sample;
-} FMODVoiceIn;
-
-static struct {
-    const char *drvname;
-    int nb_samples;
-    int freq;
-    int nb_channels;
-    int bufsize;
-    int broken_adc;
-} conf = {
-    .nb_samples  = 2048 * 2,
-    .freq        = 44100,
-    .nb_channels = 2,
-};
-
-static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
-{
-    va_list ap;
-
-    va_start (ap, fmt);
-    AUD_vlog (AUDIO_CAP, fmt, ap);
-    va_end (ap);
-
-    AUD_log (AUDIO_CAP, "Reason: %s\n",
-             FMOD_ErrorString (FSOUND_GetError ()));
-}
-
-static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
-    const char *typ,
-    const char *fmt,
-    ...
-    )
-{
-    va_list ap;
-
-    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
-
-    va_start (ap, fmt);
-    AUD_vlog (AUDIO_CAP, fmt, ap);
-    va_end (ap);
-
-    AUD_log (AUDIO_CAP, "Reason: %s\n",
-             FMOD_ErrorString (FSOUND_GetError ()));
-}
-
-static int fmod_write (SWVoiceOut *sw, void *buf, int len)
-{
-    return audio_pcm_sw_write (sw, buf, len);
-}
-
-static void fmod_clear_sample (FMODVoiceOut *fmd)
-{
-    HWVoiceOut *hw = &fmd->hw;
-    int status;
-    void *p1 = 0, *p2 = 0;
-    unsigned int len1 = 0, len2 = 0;
-
-    status = FSOUND_Sample_Lock (
-        fmd->fmod_sample,
-        0,
-        hw->samples << hw->info.shift,
-        &p1,
-        &p2,
-        &len1,
-        &len2
-        );
-
-    if (!status) {
-        fmod_logerr ("Failed to lock sample\n");
-        return;
-    }
-
-    if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
-        dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
-               len1, len2, hw->info.align + 1);
-        goto fail;
-    }
-
-    if ((len1 + len2) - (hw->samples << hw->info.shift)) {
-        dolog ("Lock returned incomplete length %d, %d\n",
-               len1 + len2, hw->samples << hw->info.shift);
-        goto fail;
-    }
-
-    audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
-
- fail:
-    status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
-    if (!status) {
-        fmod_logerr ("Failed to unlock sample\n");
-    }
-}
-
-static void fmod_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 int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
-                               unsigned int blen1, unsigned int blen2)
-{
-    int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
-    if (!status) {
-        fmod_logerr ("Failed to unlock sample\n");
-        return -1;
-    }
-    return 0;
-}
-
-static int fmod_lock_sample (
-    FSOUND_SAMPLE *sample,
-    struct audio_pcm_info *info,
-    int pos,
-    int len,
-    void **p1,
-    void **p2,
-    unsigned int *blen1,
-    unsigned int *blen2
-    )
-{
-    int status;
-
-    status = FSOUND_Sample_Lock (
-        sample,
-        pos << info->shift,
-        len << info->shift,
-        p1,
-        p2,
-        blen1,
-        blen2
-        );
-
-    if (!status) {
-        fmod_logerr ("Failed to lock sample\n");
-        return -1;
-    }
-
-    if ((*blen1 & info->align) || (*blen2 & info->align)) {
-        dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
-               *blen1, *blen2, info->align + 1);
-
-        fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2);
-
-        *p1 = NULL - 1;
-        *p2 = NULL - 1;
-        *blen1 = ~0U;
-        *blen2 = ~0U;
-        return -1;
-    }
-
-    if (!*p1 && *blen1) {
-        dolog ("warning: !p1 && blen1=%d\n", *blen1);
-        *blen1 = 0;
-    }
-
-    if (!p2 && *blen2) {
-        dolog ("warning: !p2 && blen2=%d\n", *blen2);
-        *blen2 = 0;
-    }
-
-    return 0;
-}
-
-static int fmod_run_out (HWVoiceOut *hw, int live)
-{
-    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
-    int decr;
-    void *p1 = 0, *p2 = 0;
-    unsigned int blen1 = 0, blen2 = 0;
-    unsigned int len1 = 0, len2 = 0;
-
-    if (!hw->pending_disable) {
-        return 0;
-    }
-
-    decr = live;
-
-    if (fmd->channel >= 0) {
-        int len = decr;
-        int old_pos = fmd->old_pos;
-        int ppos = FSOUND_GetCurrentPosition (fmd->channel);
-
-        if (ppos == old_pos || !ppos) {
-            return 0;
-        }
-
-        if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
-            len = ppos - old_pos;
-        }
-        else {
-            if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
-                len = hw->samples - old_pos + ppos;
-            }
-        }
-        decr = len;
-
-        if (audio_bug (AUDIO_FUNC, decr < 0)) {
-            dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
-                   decr, live, ppos, old_pos, len);
-            return 0;
-        }
-    }
-
-
-    if (!decr) {
-        return 0;
-    }
-
-    if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
-                          fmd->old_pos, decr,
-                          &p1, &p2,
-                          &blen1, &blen2)) {
-        return 0;
-    }
-
-    len1 = blen1 >> hw->info.shift;
-    len2 = blen2 >> hw->info.shift;
-    ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
-    decr = len1 + len2;
-
-    if (p1 && len1) {
-        fmod_write_sample (hw, p1, len1);
-    }
-
-    if (p2 && len2) {
-        fmod_write_sample (hw, p2, len2);
-    }
-
-    fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
-
-    fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
-    return decr;
-}
-
-static int aud_to_fmodfmt (audfmt_e fmt, int stereo)
-{
-    int mode = FSOUND_LOOP_NORMAL;
-
-    switch (fmt) {
-    case AUD_FMT_S8:
-        mode |= FSOUND_SIGNED | FSOUND_8BITS;
-        break;
-
-    case AUD_FMT_U8:
-        mode |= FSOUND_UNSIGNED | FSOUND_8BITS;
-        break;
-
-    case AUD_FMT_S16:
-        mode |= FSOUND_SIGNED | FSOUND_16BITS;
-        break;
-
-    case AUD_FMT_U16:
-        mode |= FSOUND_UNSIGNED | FSOUND_16BITS;
-        break;
-
-    default:
-        dolog ("Internal logic error: Bad audio format %d\n", fmt);
-#ifdef DEBUG_FMOD
-        abort ();
-#endif
-        mode |= FSOUND_8BITS;
-    }
-    mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
-    return mode;
-}
-
-static void fmod_fini_out (HWVoiceOut *hw)
-{
-    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
-
-    if (fmd->fmod_sample) {
-        FSOUND_Sample_Free (fmd->fmod_sample);
-        fmd->fmod_sample = 0;
-
-        if (fmd->channel >= 0) {
-            FSOUND_StopSound (fmd->channel);
-        }
-    }
-}
-
-static int fmod_init_out (HWVoiceOut *hw, struct audsettings *as)
-{
-    int mode, channel;
-    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
-    struct audsettings obt_as = *as;
-
-    mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
-    fmd->fmod_sample = FSOUND_Sample_Alloc (
-        FSOUND_FREE,            /* index */
-        conf.nb_samples,        /* length */
-        mode,                   /* mode */
-        as->freq,               /* freq */
-        255,                    /* volume */
-        128,                    /* pan */
-        255                     /* priority */
-        );
-
-    if (!fmd->fmod_sample) {
-        fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
-        return -1;
-    }
-
-    channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
-    if (channel < 0) {
-        fmod_logerr2 ("DAC", "Failed to start playing sound\n");
-        FSOUND_Sample_Free (fmd->fmod_sample);
-        return -1;
-    }
-    fmd->channel = channel;
-
-    /* FMOD always operates on little endian frames? */
-    obt_as.endianness = 0;
-    audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = conf.nb_samples;
-    return 0;
-}
-
-static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
-{
-    int status;
-    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
-
-    switch (cmd) {
-    case VOICE_ENABLE:
-        fmod_clear_sample (fmd);
-        status = FSOUND_SetPaused (fmd->channel, 0);
-        if (!status) {
-            fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
-        }
-        break;
-
-    case VOICE_DISABLE:
-        status = FSOUND_SetPaused (fmd->channel, 1);
-        if (!status) {
-            fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
-        }
-        break;
-    }
-    return 0;
-}
-
-static int fmod_init_in (HWVoiceIn *hw, struct audsettings *as)
-{
-    int mode;
-    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
-    struct audsettings obt_as = *as;
-
-    if (conf.broken_adc) {
-        return -1;
-    }
-
-    mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
-    fmd->fmod_sample = FSOUND_Sample_Alloc (
-        FSOUND_FREE,            /* index */
-        conf.nb_samples,        /* length */
-        mode,                   /* mode */
-        as->freq,               /* freq */
-        255,                    /* volume */
-        128,                    /* pan */
-        255                     /* priority */
-        );
-
-    if (!fmd->fmod_sample) {
-        fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
-        return -1;
-    }
-
-    /* FMOD always operates on little endian frames? */
-    obt_as.endianness = 0;
-    audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = conf.nb_samples;
-    return 0;
-}
-
-static void fmod_fini_in (HWVoiceIn *hw)
-{
-    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
-
-    if (fmd->fmod_sample) {
-        FSOUND_Record_Stop ();
-        FSOUND_Sample_Free (fmd->fmod_sample);
-        fmd->fmod_sample = 0;
-    }
-}
-
-static int fmod_run_in (HWVoiceIn *hw)
-{
-    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
-    int hwshift = hw->info.shift;
-    int live, dead, new_pos, len;
-    unsigned int blen1 = 0, blen2 = 0;
-    unsigned int len1, len2;
-    unsigned int decr;
-    void *p1, *p2;
-
-    live = audio_pcm_hw_get_live_in (hw);
-    dead = hw->samples - live;
-    if (!dead) {
-        return 0;
-    }
-
-    new_pos = FSOUND_Record_GetPosition ();
-    if (new_pos < 0) {
-        fmod_logerr ("Could not get recording position\n");
-        return 0;
-    }
-
-    len = audio_ring_dist (new_pos,  hw->wpos, hw->samples);
-    if (!len) {
-        return 0;
-    }
-    len = audio_MIN (len, dead);
-
-    if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
-                          hw->wpos, len,
-                          &p1, &p2,
-                          &blen1, &blen2)) {
-        return 0;
-    }
-
-    len1 = blen1 >> hwshift;
-    len2 = blen2 >> hwshift;
-    decr = len1 + len2;
-
-    if (p1 && blen1) {
-        hw->conv (hw->conv_buf + hw->wpos, p1, len1);
-    }
-    if (p2 && len2) {
-        hw->conv (hw->conv_buf, p2, len2);
-    }
-
-    fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
-    hw->wpos = (hw->wpos + decr) % hw->samples;
-    return decr;
-}
-
-static struct {
-    const char *name;
-    int type;
-} drvtab[] = {
-    { .name = "none",   .type = FSOUND_OUTPUT_NOSOUND },
-#ifdef _WIN32
-    { .name = "winmm",  .type = FSOUND_OUTPUT_WINMM   },
-    { .name = "dsound", .type = FSOUND_OUTPUT_DSOUND  },
-    { .name = "a3d",    .type = FSOUND_OUTPUT_A3D     },
-    { .name = "asio",   .type = FSOUND_OUTPUT_ASIO    },
-#endif
-#ifdef __linux__
-    { .name = "oss",    .type = FSOUND_OUTPUT_OSS     },
-    { .name = "alsa",   .type = FSOUND_OUTPUT_ALSA    },
-    { .name = "esd",    .type = FSOUND_OUTPUT_ESD     },
-#endif
-#ifdef __APPLE__
-    { .name = "mac",    .type = FSOUND_OUTPUT_MAC     },
-#endif
-#if 0
-    { .name = "xbox",   .type = FSOUND_OUTPUT_XBOX    },
-    { .name = "ps2",    .type = FSOUND_OUTPUT_PS2     },
-    { .name = "gcube",  .type = FSOUND_OUTPUT_GC      },
-#endif
-    { .name = "none-realtime", .type = FSOUND_OUTPUT_NOSOUND_NONREALTIME }
-};
-
-static void *fmod_audio_init (void)
-{
-    size_t i;
-    double ver;
-    int status;
-    int output_type = -1;
-    const char *drv = conf.drvname;
-
-    ver = FSOUND_GetVersion ();
-    if (ver < FMOD_VERSION) {
-        dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
-        return NULL;
-    }
-
-#ifdef __linux__
-    if (ver < 3.75) {
-        dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
-               "ADC will be disabled.\n");
-        conf.broken_adc = 1;
-    }
-#endif
-
-    if (drv) {
-        int found = 0;
-        for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
-            if (!strcmp (drv, drvtab[i].name)) {
-                output_type = drvtab[i].type;
-                found = 1;
-                break;
-            }
-        }
-        if (!found) {
-            dolog ("Unknown FMOD driver `%s'\n", drv);
-            dolog ("Valid drivers:\n");
-            for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
-                dolog ("  %s\n", drvtab[i].name);
-            }
-        }
-    }
-
-    if (output_type != -1) {
-        status = FSOUND_SetOutput (output_type);
-        if (!status) {
-            fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
-            return NULL;
-        }
-    }
-
-    if (conf.bufsize) {
-        status = FSOUND_SetBufferSize (conf.bufsize);
-        if (!status) {
-            fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
-        }
-    }
-
-    status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
-    if (!status) {
-        fmod_logerr ("FSOUND_Init failed\n");
-        return NULL;
-    }
-
-    return &conf;
-}
-
-static int fmod_read (SWVoiceIn *sw, void *buf, int size)
-{
-    return audio_pcm_sw_read (sw, buf, size);
-}
-
-static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...)
-{
-    int status;
-    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
-
-    switch (cmd) {
-    case VOICE_ENABLE:
-        status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
-        if (!status) {
-            fmod_logerr ("Failed to start recording\n");
-        }
-        break;
-
-    case VOICE_DISABLE:
-        status = FSOUND_Record_Stop ();
-        if (!status) {
-            fmod_logerr ("Failed to stop recording\n");
-        }
-        break;
-    }
-    return 0;
-}
-
-static void fmod_audio_fini (void *opaque)
-{
-    (void) opaque;
-    FSOUND_Close ();
-}
-
-static struct audio_option fmod_options[] = {
-    {
-        .name  = "DRV",
-        .tag   = AUD_OPT_STR,
-        .valp  = &conf.drvname,
-        .descr = "FMOD driver"
-    },
-    {
-        .name  = "FREQ",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.freq,
-        .descr = "Default frequency"
-    },
-    {
-        .name  = "SAMPLES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.nb_samples,
-        .descr = "Buffer size in samples"
-    },
-    {
-        .name  = "CHANNELS",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.nb_channels,
-        .descr = "Number of default channels (1 - mono, 2 - stereo)"
-    },
-    {
-        .name  = "BUFSIZE",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.bufsize,
-        .descr = "(undocumented)"
-    },
-    { /* End of list */ }
-};
-
-static struct audio_pcm_ops fmod_pcm_ops = {
-    .init_out = fmod_init_out,
-    .fini_out = fmod_fini_out,
-    .run_out  = fmod_run_out,
-    .write    = fmod_write,
-    .ctl_out  = fmod_ctl_out,
-
-    .init_in  = fmod_init_in,
-    .fini_in  = fmod_fini_in,
-    .run_in   = fmod_run_in,
-    .read     = fmod_read,
-    .ctl_in   = fmod_ctl_in
-};
-
-struct audio_driver fmod_audio_driver = {
-    .name           = "fmod",
-    .descr          = "FMOD 3.xx http://www.fmod.org",
-    .options        = fmod_options,
-    .init           = fmod_audio_init,
-    .fini           = fmod_audio_fini,
-    .pcm_ops        = &fmod_pcm_ops,
-    .can_be_default = 1,
-    .max_voices_out = INT_MAX,
-    .max_voices_in  = INT_MAX,
-    .voice_size_out = sizeof (FMODVoiceOut),
-    .voice_size_in  = sizeof (FMODVoiceIn)
-};
diff --git a/audio/noaudio.c b/audio/noaudio.c
index cb386620ae..50db1f344b 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -63,7 +63,7 @@ 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)
+static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
 {
     audio_pcm_init_info (&hw->info, as);
     hw->samples = 1024;
@@ -82,7 +82,7 @@ static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return 0;
 }
 
-static int no_init_in (HWVoiceIn *hw, struct audsettings *as)
+static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 {
     audio_pcm_init_info (&hw->info, as);
     hw->samples = 1024;
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index b9c6b30ca1..11e76a15a2 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -30,6 +30,7 @@
 #include "qemu/main-loop.h"
 #include "qemu/host-utils.h"
 #include "audio.h"
+#include "trace.h"
 
 #define AUDIO_CAP "oss"
 #include "audio_int.h"
@@ -38,6 +39,16 @@
 #define USE_DSP_POLICY
 #endif
 
+typedef struct OSSConf {
+    int try_mmap;
+    int nfrags;
+    int fragsize;
+    const char *devpath_out;
+    const char *devpath_in;
+    int exclusive;
+    int policy;
+} OSSConf;
+
 typedef struct OSSVoiceOut {
     HWVoiceOut hw;
     void *pcm_buf;
@@ -47,6 +58,7 @@ typedef struct OSSVoiceOut {
     int fragsize;
     int mmapped;
     int pending;
+    OSSConf *conf;
 } OSSVoiceOut;
 
 typedef struct OSSVoiceIn {
@@ -55,28 +67,9 @@ typedef struct OSSVoiceIn {
     int fd;
     int nfrags;
     int fragsize;
+    OSSConf *conf;
 } OSSVoiceIn;
 
-static struct {
-    int try_mmap;
-    int nfrags;
-    int fragsize;
-    const char *devpath_out;
-    const char *devpath_in;
-    int debug;
-    int exclusive;
-    int policy;
-} conf = {
-    .try_mmap = 0,
-    .nfrags = 4,
-    .fragsize = 4096,
-    .devpath_out = "/dev/dsp",
-    .devpath_in = "/dev/dsp",
-    .debug = 0,
-    .exclusive = 0,
-    .policy = 5
-};
-
 struct oss_params {
     int freq;
     audfmt_e fmt;
@@ -272,18 +265,18 @@ static int oss_get_version (int fd, int *version, const char *typ)
 #endif
 
 static int oss_open (int in, struct oss_params *req,
-                     struct oss_params *obt, int *pfd)
+                     struct oss_params *obt, int *pfd, OSSConf* conf)
 {
     int fd;
-    int oflags = conf.exclusive ? O_EXCL : 0;
+    int oflags = conf->exclusive ? O_EXCL : 0;
     audio_buf_info abinfo;
     int fmt, freq, nchannels;
     int setfragment = 1;
-    const char *dspname = in ? conf.devpath_in : conf.devpath_out;
+    const char *dspname = in ? conf->devpath_in : conf->devpath_out;
     const char *typ = in ? "ADC" : "DAC";
 
     /* Kludge needed to have working mmap on Linux */
-    oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
+    oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
 
     fd = open (dspname, oflags | O_NONBLOCK);
     if (-1 == fd) {
@@ -317,20 +310,18 @@ static int oss_open (int in, struct oss_params *req,
     }
 
 #ifdef USE_DSP_POLICY
-    if (conf.policy >= 0) {
+    if (conf->policy >= 0) {
         int version;
 
         if (!oss_get_version (fd, &version, typ)) {
-            if (conf.debug) {
-                dolog ("OSS version = %#x\n", version);
-            }
+            trace_oss_version(version);
 
             if (version >= 0x040000) {
-                int policy = conf.policy;
+                int policy = conf->policy;
                 if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
                     oss_logerr2 (errno, typ,
                                  "Failed to set timing policy to %d\n",
-                                 conf.policy);
+                                 conf->policy);
                     goto err;
                 }
                 setfragment = 0;
@@ -458,19 +449,12 @@ static int oss_run_out (HWVoiceOut *hw, int live)
         }
 
         if (abinfo.bytes > bufsize) {
-            if (conf.debug) {
-                dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
-                       "please report your OS/audio hw to av1474@comtv.ru\n",
-                       abinfo.bytes, bufsize);
-            }
+            trace_oss_invalid_available_size(abinfo.bytes, bufsize);
             abinfo.bytes = bufsize;
         }
 
         if (abinfo.bytes < 0) {
-            if (conf.debug) {
-                dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
-                       abinfo.bytes, bufsize);
-            }
+            trace_oss_invalid_available_size(abinfo.bytes, bufsize);
             return 0;
         }
 
@@ -510,7 +494,8 @@ static void oss_fini_out (HWVoiceOut *hw)
     }
 }
 
-static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
+                        void *drv_opaque)
 {
     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
     struct oss_params req, obt;
@@ -519,16 +504,17 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
     int fd;
     audfmt_e effective_fmt;
     struct audsettings obt_as;
+    OSSConf *conf = drv_opaque;
 
     oss->fd = -1;
 
     req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
-    req.fragsize = conf.fragsize;
-    req.nfrags = conf.nfrags;
+    req.fragsize = conf->fragsize;
+    req.nfrags = conf->nfrags;
 
-    if (oss_open (0, &req, &obt, &fd)) {
+    if (oss_open (0, &req, &obt, &fd, conf)) {
         return -1;
     }
 
@@ -555,7 +541,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
 
     oss->mmapped = 0;
-    if (conf.try_mmap) {
+    if (conf->try_mmap) {
         oss->pcm_buf = mmap (
             NULL,
             hw->samples << hw->info.shift,
@@ -615,6 +601,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
     }
 
     oss->fd = fd;
+    oss->conf = conf;
     return 0;
 }
 
@@ -677,7 +664,7 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return 0;
 }
 
-static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
+static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 {
     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
     struct oss_params req, obt;
@@ -686,15 +673,16 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
     int fd;
     audfmt_e effective_fmt;
     struct audsettings obt_as;
+    OSSConf *conf = drv_opaque;
 
     oss->fd = -1;
 
     req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
-    req.fragsize = conf.fragsize;
-    req.nfrags = conf.nfrags;
-    if (oss_open (1, &req, &obt, &fd)) {
+    req.fragsize = conf->fragsize;
+    req.nfrags = conf->nfrags;
+    if (oss_open (1, &req, &obt, &fd, conf)) {
         return -1;
     }
 
@@ -728,6 +716,7 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
     }
 
     oss->fd = fd;
+    oss->conf = conf;
     return 0;
 }
 
@@ -847,71 +836,78 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
     return 0;
 }
 
+static OSSConf glob_conf = {
+    .try_mmap = 0,
+    .nfrags = 4,
+    .fragsize = 4096,
+    .devpath_out = "/dev/dsp",
+    .devpath_in = "/dev/dsp",
+    .exclusive = 0,
+    .policy = 5
+};
+
 static void *oss_audio_init (void)
 {
-    if (access(conf.devpath_in, R_OK | W_OK) < 0 ||
-        access(conf.devpath_out, R_OK | W_OK) < 0) {
+    OSSConf *conf = g_malloc(sizeof(OSSConf));
+    *conf = glob_conf;
+
+    if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
+        access(conf->devpath_out, R_OK | W_OK) < 0) {
         return NULL;
     }
-    return &conf;
+    return conf;
 }
 
 static void oss_audio_fini (void *opaque)
 {
-    (void) opaque;
+    g_free(opaque);
 }
 
 static struct audio_option oss_options[] = {
     {
         .name  = "FRAGSIZE",
         .tag   = AUD_OPT_INT,
-        .valp  = &conf.fragsize,
+        .valp  = &glob_conf.fragsize,
         .descr = "Fragment size in bytes"
     },
     {
         .name  = "NFRAGS",
         .tag   = AUD_OPT_INT,
-        .valp  = &conf.nfrags,
+        .valp  = &glob_conf.nfrags,
         .descr = "Number of fragments"
     },
     {
         .name  = "MMAP",
         .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.try_mmap,
+        .valp  = &glob_conf.try_mmap,
         .descr = "Try using memory mapped access"
     },
     {
         .name  = "DAC_DEV",
         .tag   = AUD_OPT_STR,
-        .valp  = &conf.devpath_out,
+        .valp  = &glob_conf.devpath_out,
         .descr = "Path to DAC device"
     },
     {
         .name  = "ADC_DEV",
         .tag   = AUD_OPT_STR,
-        .valp  = &conf.devpath_in,
+        .valp  = &glob_conf.devpath_in,
         .descr = "Path to ADC device"
     },
     {
         .name  = "EXCLUSIVE",
         .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.exclusive,
+        .valp  = &glob_conf.exclusive,
         .descr = "Open device in exclusive mode (vmix wont work)"
     },
 #ifdef USE_DSP_POLICY
     {
         .name  = "POLICY",
         .tag   = AUD_OPT_INT,
-        .valp  = &conf.policy,
+        .valp  = &glob_conf.policy,
         .descr = "Set the timing policy of the device, -1 to use fragment mode",
     },
 #endif
-    {
-        .name  = "DEBUG",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.debug,
-        .descr = "Turn on some debugging messages"
-    },
     { /* End of list */ }
 };
 
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 90ff24500b..fea607166f 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -9,6 +9,19 @@
 #include "audio_pt_int.h"
 
 typedef struct {
+    int samples;
+    char *server;
+    char *sink;
+    char *source;
+} PAConf;
+
+typedef struct {
+    PAConf conf;
+    pa_threaded_mainloop *mainloop;
+    pa_context *context;
+} paaudio;
+
+typedef struct {
     HWVoiceOut hw;
     int done;
     int live;
@@ -17,6 +30,7 @@ typedef struct {
     pa_stream *stream;
     void *pcm_buf;
     struct audio_pt pt;
+    paaudio *g;
 } PAVoiceOut;
 
 typedef struct {
@@ -30,20 +44,10 @@ typedef struct {
     struct audio_pt pt;
     const void *read_data;
     size_t read_index, read_length;
+    paaudio *g;
 } PAVoiceIn;
 
-typedef struct {
-    int samples;
-    char *server;
-    char *sink;
-    char *source;
-    pa_threaded_mainloop *mainloop;
-    pa_context *context;
-} paaudio;
-
-static paaudio glob_paaudio = {
-    .samples = 4096,
-};
+static void qpa_audio_fini(void *opaque);
 
 static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
 {
@@ -106,7 +110,7 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
 
 static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
 {
-    paaudio *g = &glob_paaudio;
+    paaudio *g = p->g;
 
     pa_threaded_mainloop_lock (g->mainloop);
 
@@ -160,7 +164,7 @@ unlock_and_fail:
 
 static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
 {
-    paaudio *g = &glob_paaudio;
+    paaudio *g = p->g;
 
     pa_threaded_mainloop_lock (g->mainloop);
 
@@ -222,7 +226,7 @@ static void *qpa_thread_out (void *arg)
             }
         }
 
-        decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2);
+        decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
         rpos = pa->rpos;
 
         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
@@ -314,7 +318,7 @@ static void *qpa_thread_in (void *arg)
             }
         }
 
-        incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2);
+        incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
         wpos = pa->wpos;
 
         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
@@ -430,7 +434,7 @@ static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
 
 static void context_state_cb (pa_context *c, void *userdata)
 {
-    paaudio *g = &glob_paaudio;
+    paaudio *g = userdata;
 
     switch (pa_context_get_state(c)) {
     case PA_CONTEXT_READY:
@@ -449,7 +453,7 @@ static void context_state_cb (pa_context *c, void *userdata)
 
 static void stream_state_cb (pa_stream *s, void * userdata)
 {
-    paaudio *g = &glob_paaudio;
+    paaudio *g = userdata;
 
     switch (pa_stream_get_state (s)) {
 
@@ -467,23 +471,21 @@ 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 = &glob_paaudio;
+    paaudio *g = userdata;
 
     pa_threaded_mainloop_signal (g->mainloop, 0);
 }
 
 static pa_stream *qpa_simple_new (
-        const char *server,
+        paaudio *g,
         const char *name,
         pa_stream_direction_t dir,
         const char *dev,
-        const char *stream_name,
         const pa_sample_spec *ss,
         const pa_channel_map *map,
         const pa_buffer_attr *attr,
         int *rerror)
 {
-    paaudio *g = &glob_paaudio;
     int r;
     pa_stream *stream;
 
@@ -534,13 +536,15 @@ fail:
     return NULL;
 }
 
-static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
+                        void *drv_opaque)
 {
     int error;
-    static pa_sample_spec ss;
-    static pa_buffer_attr ba;
+    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;
@@ -558,11 +562,10 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
 
     pa->stream = qpa_simple_new (
-        glob_paaudio.server,
+        g,
         "qemu",
         PA_STREAM_PLAYBACK,
-        glob_paaudio.sink,
-        "pcm.playback",
+        g->conf.sink,
         &ss,
         NULL,                   /* channel map */
         &ba,                    /* buffering attributes */
@@ -574,7 +577,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
     }
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = glob_paaudio.samples;
+    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) {
@@ -601,12 +604,13 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
     return -1;
 }
 
-static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
+static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 {
     int error;
-    static pa_sample_spec ss;
+    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;
@@ -615,11 +619,10 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
 
     pa->stream = qpa_simple_new (
-        glob_paaudio.server,
+        g,
         "qemu",
         PA_STREAM_RECORD,
-        glob_paaudio.source,
-        "pcm.capture",
+        g->conf.source,
         &ss,
         NULL,                   /* channel map */
         NULL,                   /* buffering attributes */
@@ -631,7 +634,7 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
     }
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = glob_paaudio.samples;
+    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) {
@@ -703,7 +706,7 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
     PAVoiceOut *pa = (PAVoiceOut *) hw;
     pa_operation *op;
     pa_cvolume v;
-    paaudio *g = &glob_paaudio;
+    paaudio *g = pa->g;
 
 #ifdef PA_CHECK_VERSION    /* macro is present in 0.9.16+ */
     pa_cvolume_init (&v);  /* function is present in 0.9.13+ */
@@ -755,7 +758,7 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
     PAVoiceIn *pa = (PAVoiceIn *) hw;
     pa_operation *op;
     pa_cvolume v;
-    paaudio *g = &glob_paaudio;
+    paaudio *g = pa->g;
 
 #ifdef PA_CHECK_VERSION
     pa_cvolume_init (&v);
@@ -805,23 +808,31 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
 }
 
 /* common */
+static PAConf glob_conf = {
+    .samples = 4096,
+};
+
 static void *qpa_audio_init (void)
 {
-    paaudio *g = &glob_paaudio;
+    paaudio *g = g_malloc(sizeof(paaudio));
+    g->conf = glob_conf;
+    g->mainloop = NULL;
+    g->context = NULL;
 
     g->mainloop = pa_threaded_mainloop_new ();
     if (!g->mainloop) {
         goto fail;
     }
 
-    g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server);
+    g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
+                                 g->conf.server);
     if (!g->context) {
         goto fail;
     }
 
     pa_context_set_state_callback (g->context, context_state_cb, g);
 
-    if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) {
+    if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) {
         qpa_logerr (pa_context_errno (g->context),
                     "pa_context_connect() failed\n");
         goto fail;
@@ -854,12 +865,13 @@ static void *qpa_audio_init (void)
 
     pa_threaded_mainloop_unlock (g->mainloop);
 
-    return &glob_paaudio;
+    return g;
 
 unlock_and_fail:
     pa_threaded_mainloop_unlock (g->mainloop);
 fail:
     AUD_log (AUDIO_CAP, "Failed to initialize PA context");
+    qpa_audio_fini(g);
     return NULL;
 }
 
@@ -874,39 +886,38 @@ static void qpa_audio_fini (void *opaque)
     if (g->context) {
         pa_context_disconnect (g->context);
         pa_context_unref (g->context);
-        g->context = NULL;
     }
 
     if (g->mainloop) {
         pa_threaded_mainloop_free (g->mainloop);
     }
 
-    g->mainloop = NULL;
+    g_free(g);
 }
 
 struct audio_option qpa_options[] = {
     {
         .name  = "SAMPLES",
         .tag   = AUD_OPT_INT,
-        .valp  = &glob_paaudio.samples,
+        .valp  = &glob_conf.samples,
         .descr = "buffer size in samples"
     },
     {
         .name  = "SERVER",
         .tag   = AUD_OPT_STR,
-        .valp  = &glob_paaudio.server,
+        .valp  = &glob_conf.server,
         .descr = "server address"
     },
     {
         .name  = "SINK",
         .tag   = AUD_OPT_STR,
-        .valp  = &glob_paaudio.sink,
+        .valp  = &glob_conf.sink,
         .descr = "sink device name"
     },
     {
         .name  = "SOURCE",
         .tag   = AUD_OPT_STR,
-        .valp  = &glob_paaudio.source,
+        .valp  = &glob_conf.source,
         .descr = "source device name"
     },
     { /* End of list */ }
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index d24daa5ead..1140f2ea0a 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -55,6 +55,7 @@ static struct SDLAudioState {
     SDL_mutex *mutex;
     SDL_sem *sem;
     int initialized;
+    bool driver_created;
 } glob_sdl;
 typedef struct SDLAudioState SDLAudioState;
 
@@ -332,7 +333,8 @@ static void sdl_fini_out (HWVoiceOut *hw)
     sdl_close (&glob_sdl);
 }
 
-static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
+                        void *drv_opaque)
 {
     SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
     SDLAudioState *s = &glob_sdl;
@@ -392,6 +394,10 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
 static void *sdl_audio_init (void)
 {
     SDLAudioState *s = &glob_sdl;
+    if (s->driver_created) {
+        sdl_logerr("Can't create multiple sdl backends\n");
+        return NULL;
+    }
 
     if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
         sdl_logerr ("SDL failed to initialize audio subsystem\n");
@@ -413,6 +419,7 @@ static void *sdl_audio_init (void)
         return NULL;
     }
 
+    s->driver_created = true;
     return s;
 }
 
@@ -423,6 +430,7 @@ static void sdl_audio_fini (void *opaque)
     SDL_DestroySemaphore (s->sem);
     SDL_DestroyMutex (s->mutex);
     SDL_QuitSubSystem (SDL_INIT_AUDIO);
+    s->driver_created = false;
 }
 
 static struct audio_option sdl_options[] = {
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index 7b79bedca2..5c6f726757 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -115,7 +115,8 @@ static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
 
 /* playback */
 
-static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
+static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
+                         void *drv_opaque)
 {
     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
     struct audsettings settings;
@@ -243,7 +244,7 @@ static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
 
 /* record */
 
-static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
+static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 {
     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
     struct audsettings settings;
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 6846a1a9f7..c586020c59 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -36,15 +36,10 @@ typedef struct WAVVoiceOut {
     int total_samples;
 } WAVVoiceOut;
 
-static struct {
+typedef struct {
     struct audsettings settings;
     const char *wav_path;
-} conf = {
-    .settings.freq      = 44100,
-    .settings.nchannels = 2,
-    .settings.fmt       = AUD_FMT_S16,
-    .wav_path           = "qemu.wav"
-};
+} WAVConf;
 
 static int wav_run_out (HWVoiceOut *hw, int live)
 {
@@ -105,7 +100,8 @@ static void le_store (uint8_t *buf, uint32_t val, int len)
     }
 }
 
-static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
+                        void *drv_opaque)
 {
     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
     int bits16 = 0, stereo = 0;
@@ -115,9 +111,8 @@ static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
     };
-    struct audsettings wav_as = conf.settings;
-
-    (void) as;
+    WAVConf *conf = drv_opaque;
+    struct audsettings wav_as = conf->settings;
 
     stereo = wav_as.nchannels == 2;
     switch (wav_as.fmt) {
@@ -155,10 +150,10 @@ static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
     le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
     le_store (hdr + 32, 1 << (bits16 + stereo), 2);
 
-    wav->f = fopen (conf.wav_path, "wb");
+    wav->f = fopen (conf->wav_path, "wb");
     if (!wav->f) {
         dolog ("Failed to open wave file `%s'\nReason: %s\n",
-               conf.wav_path, strerror (errno));
+               conf->wav_path, strerror (errno));
         g_free (wav->pcm_buf);
         wav->pcm_buf = NULL;
         return -1;
@@ -226,40 +221,49 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return 0;
 }
 
+static WAVConf glob_conf = {
+    .settings.freq      = 44100,
+    .settings.nchannels = 2,
+    .settings.fmt       = AUD_FMT_S16,
+    .wav_path           = "qemu.wav"
+};
+
 static void *wav_audio_init (void)
 {
-    return &conf;
+    WAVConf *conf = g_malloc(sizeof(WAVConf));
+    *conf = glob_conf;
+    return conf;
 }
 
 static void wav_audio_fini (void *opaque)
 {
-    (void) opaque;
     ldebug ("wav_fini");
+    g_free(opaque);
 }
 
 static struct audio_option wav_options[] = {
     {
         .name  = "FREQUENCY",
         .tag   = AUD_OPT_INT,
-        .valp  = &conf.settings.freq,
+        .valp  = &glob_conf.settings.freq,
         .descr = "Frequency"
     },
     {
         .name  = "FORMAT",
         .tag   = AUD_OPT_FMT,
-        .valp  = &conf.settings.fmt,
+        .valp  = &glob_conf.settings.fmt,
         .descr = "Format"
     },
     {
         .name  = "DAC_FIXED_CHANNELS",
         .tag   = AUD_OPT_INT,
-        .valp  = &conf.settings.nchannels,
+        .valp  = &glob_conf.settings.nchannels,
         .descr = "Number of channels (1 - mono, 2 - stereo)"
     },
     {
         .name  = "PATH",
         .tag   = AUD_OPT_STR,
-        .valp  = &conf.wav_path,
+        .valp  = &glob_conf.wav_path,
         .descr = "Path to wave file"
     },
     { /* End of list */ }
diff --git a/audio/winwaveaudio.c b/audio/winwaveaudio.c
deleted file mode 100644
index 8dbd145ca1..0000000000
--- a/audio/winwaveaudio.c
+++ /dev/null
@@ -1,717 +0,0 @@
-/* public domain */
-
-#include "qemu-common.h"
-#include "sysemu/sysemu.h"
-#include "audio.h"
-
-#define AUDIO_CAP "winwave"
-#include "audio_int.h"
-
-#include <windows.h>
-#include <mmsystem.h>
-
-#include "audio_win_int.h"
-
-static struct {
-    int dac_headers;
-    int dac_samples;
-    int adc_headers;
-    int adc_samples;
-} conf = {
-    .dac_headers = 4,
-    .dac_samples = 1024,
-    .adc_headers = 4,
-    .adc_samples = 1024
-};
-
-typedef struct {
-    HWVoiceOut hw;
-    HWAVEOUT hwo;
-    WAVEHDR *hdrs;
-    HANDLE event;
-    void *pcm_buf;
-    int avail;
-    int pending;
-    int curhdr;
-    int paused;
-    CRITICAL_SECTION crit_sect;
-} WaveVoiceOut;
-
-typedef struct {
-    HWVoiceIn hw;
-    HWAVEIN hwi;
-    WAVEHDR *hdrs;
-    HANDLE event;
-    void *pcm_buf;
-    int curhdr;
-    int paused;
-    int rpos;
-    int avail;
-    CRITICAL_SECTION crit_sect;
-} WaveVoiceIn;
-
-static void winwave_log_mmresult (MMRESULT mr)
-{
-    const char *str = "BUG";
-
-    switch (mr) {
-    case MMSYSERR_NOERROR:
-        str = "Success";
-        break;
-
-    case MMSYSERR_INVALHANDLE:
-        str = "Specified device handle is invalid";
-        break;
-
-    case MMSYSERR_BADDEVICEID:
-        str = "Specified device id is out of range";
-        break;
-
-    case MMSYSERR_NODRIVER:
-        str = "No device driver is present";
-        break;
-
-    case MMSYSERR_NOMEM:
-        str = "Unable to allocate or lock memory";
-        break;
-
-    case WAVERR_SYNC:
-        str = "Device is synchronous but waveOutOpen was called "
-            "without using the WINWAVE_ALLOWSYNC flag";
-        break;
-
-    case WAVERR_UNPREPARED:
-        str = "The data block pointed to by the pwh parameter "
-            "hasn't been prepared";
-        break;
-
-    case WAVERR_STILLPLAYING:
-        str = "There are still buffers in the queue";
-        break;
-
-    default:
-        dolog ("Reason: Unknown (MMRESULT %#x)\n", mr);
-        return;
-    }
-
-    dolog ("Reason: %s\n", str);
-}
-
-static void GCC_FMT_ATTR (2, 3) winwave_logerr (
-    MMRESULT mr,
-    const char *fmt,
-    ...
-    )
-{
-    va_list ap;
-
-    va_start (ap, fmt);
-    AUD_vlog (AUDIO_CAP, fmt, ap);
-    va_end (ap);
-
-    AUD_log (NULL, " failed\n");
-    winwave_log_mmresult (mr);
-}
-
-static void winwave_anal_close_out (WaveVoiceOut *wave)
-{
-    MMRESULT mr;
-
-    mr = waveOutClose (wave->hwo);
-    if (mr != MMSYSERR_NOERROR) {
-        winwave_logerr (mr, "waveOutClose");
-    }
-    wave->hwo = NULL;
-}
-
-static void CALLBACK winwave_callback_out (
-    HWAVEOUT hwo,
-    UINT msg,
-    DWORD_PTR dwInstance,
-    DWORD_PTR dwParam1,
-    DWORD_PTR dwParam2
-    )
-{
-    WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance;
-
-    switch (msg) {
-    case WOM_DONE:
-        {
-            WAVEHDR *h = (WAVEHDR *) dwParam1;
-            if (!h->dwUser) {
-                h->dwUser = 1;
-                EnterCriticalSection (&wave->crit_sect);
-                {
-                    wave->avail += conf.dac_samples;
-                }
-                LeaveCriticalSection (&wave->crit_sect);
-                if (wave->hw.poll_mode) {
-                    if (!SetEvent (wave->event)) {
-                        dolog ("DAC SetEvent failed %lx\n", GetLastError ());
-                    }
-                }
-            }
-        }
-        break;
-
-    case WOM_CLOSE:
-    case WOM_OPEN:
-        break;
-
-    default:
-        dolog ("unknown wave out callback msg %x\n", msg);
-    }
-}
-
-static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as)
-{
-    int i;
-    int err;
-    MMRESULT mr;
-    WAVEFORMATEX wfx;
-    WaveVoiceOut *wave;
-
-    wave = (WaveVoiceOut *) hw;
-
-    InitializeCriticalSection (&wave->crit_sect);
-
-    err = waveformat_from_audio_settings (&wfx, as);
-    if (err) {
-        goto err0;
-    }
-
-    mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx,
-                      (DWORD_PTR) winwave_callback_out,
-                      (DWORD_PTR) wave, CALLBACK_FUNCTION);
-    if (mr != MMSYSERR_NOERROR) {
-        winwave_logerr (mr, "waveOutOpen");
-        goto err1;
-    }
-
-    wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
-                               sizeof (*wave->hdrs));
-    if (!wave->hdrs) {
-        goto err2;
-    }
-
-    audio_pcm_init_info (&hw->info, as);
-    hw->samples = conf.dac_samples * conf.dac_headers;
-    wave->avail = hw->samples;
-
-    wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.dac_samples,
-                                  conf.dac_headers << hw->info.shift);
-    if (!wave->pcm_buf) {
-        goto err3;
-    }
-
-    for (i = 0; i < conf.dac_headers; ++i) {
-        WAVEHDR *h = &wave->hdrs[i];
-
-        h->dwUser = 0;
-        h->dwBufferLength = conf.dac_samples << hw->info.shift;
-        h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
-        h->dwFlags = 0;
-
-        mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h));
-        if (mr != MMSYSERR_NOERROR) {
-            winwave_logerr (mr, "waveOutPrepareHeader(%d)", i);
-            goto err4;
-        }
-    }
-
-    return 0;
-
- err4:
-    g_free (wave->pcm_buf);
- err3:
-    g_free (wave->hdrs);
- err2:
-    winwave_anal_close_out (wave);
- err1:
- err0:
-    return -1;
-}
-
-static int winwave_write (SWVoiceOut *sw, void *buf, int len)
-{
-    return audio_pcm_sw_write (sw, buf, len);
-}
-
-static int winwave_run_out (HWVoiceOut *hw, int live)
-{
-    WaveVoiceOut *wave = (WaveVoiceOut *) hw;
-    int decr;
-    int doreset;
-
-    EnterCriticalSection (&wave->crit_sect);
-    {
-        decr = audio_MIN (live, wave->avail);
-        decr = audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->pending);
-        wave->pending += decr;
-        wave->avail -= decr;
-    }
-    LeaveCriticalSection (&wave->crit_sect);
-
-    doreset = hw->poll_mode && (wave->pending >= conf.dac_samples);
-    if (doreset && !ResetEvent (wave->event)) {
-        dolog ("DAC ResetEvent failed %lx\n", GetLastError ());
-    }
-
-    while (wave->pending >= conf.dac_samples) {
-        MMRESULT mr;
-        WAVEHDR *h = &wave->hdrs[wave->curhdr];
-
-        h->dwUser = 0;
-        mr = waveOutWrite (wave->hwo, h, sizeof (*h));
-        if (mr != MMSYSERR_NOERROR) {
-            winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr);
-            break;
-        }
-
-        wave->pending -= conf.dac_samples;
-        wave->curhdr = (wave->curhdr + 1) % conf.dac_headers;
-    }
-
-    return decr;
-}
-
-static void winwave_poll (void *opaque)
-{
-    (void) opaque;
-    audio_run ("winwave_poll");
-}
-
-static void winwave_fini_out (HWVoiceOut *hw)
-{
-    int i;
-    MMRESULT mr;
-    WaveVoiceOut *wave = (WaveVoiceOut *) hw;
-
-    mr = waveOutReset (wave->hwo);
-    if (mr != MMSYSERR_NOERROR) {
-        winwave_logerr (mr, "waveOutReset");
-    }
-
-    for (i = 0; i < conf.dac_headers; ++i) {
-        mr = waveOutUnprepareHeader (wave->hwo, &wave->hdrs[i],
-                                     sizeof (wave->hdrs[i]));
-        if (mr != MMSYSERR_NOERROR) {
-            winwave_logerr (mr, "waveOutUnprepareHeader(%d)", i);
-        }
-    }
-
-    winwave_anal_close_out (wave);
-
-    if (wave->event) {
-        qemu_del_wait_object (wave->event, winwave_poll, wave);
-        if (!CloseHandle (wave->event)) {
-            dolog ("DAC CloseHandle failed %lx\n", GetLastError ());
-        }
-        wave->event = NULL;
-    }
-
-    g_free (wave->pcm_buf);
-    wave->pcm_buf = NULL;
-
-    g_free (wave->hdrs);
-    wave->hdrs = NULL;
-}
-
-static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...)
-{
-    MMRESULT mr;
-    WaveVoiceOut *wave = (WaveVoiceOut *) hw;
-
-    switch (cmd) {
-    case VOICE_ENABLE:
-        {
-            va_list ap;
-            int poll_mode;
-
-            va_start (ap, cmd);
-            poll_mode = va_arg (ap, int);
-            va_end (ap);
-
-            if (poll_mode && !wave->event) {
-                wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
-                if (!wave->event) {
-                    dolog ("DAC CreateEvent: %lx, poll mode will be disabled\n",
-                           GetLastError ());
-                }
-            }
-
-            if (wave->event) {
-                int ret;
-
-                ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
-                hw->poll_mode = (ret == 0);
-            }
-            else {
-                hw->poll_mode = 0;
-            }
-            wave->paused = 0;
-        }
-        return 0;
-
-    case VOICE_DISABLE:
-        if (!wave->paused) {
-            mr = waveOutReset (wave->hwo);
-            if (mr != MMSYSERR_NOERROR) {
-                winwave_logerr (mr, "waveOutReset");
-            }
-            else {
-                wave->paused = 1;
-            }
-        }
-        if (wave->event) {
-            qemu_del_wait_object (wave->event, winwave_poll, wave);
-        }
-        return 0;
-    }
-    return -1;
-}
-
-static void winwave_anal_close_in (WaveVoiceIn *wave)
-{
-    MMRESULT mr;
-
-    mr = waveInClose (wave->hwi);
-    if (mr != MMSYSERR_NOERROR) {
-        winwave_logerr (mr, "waveInClose");
-    }
-    wave->hwi = NULL;
-}
-
-static void CALLBACK winwave_callback_in (
-    HWAVEIN *hwi,
-    UINT msg,
-    DWORD_PTR dwInstance,
-    DWORD_PTR dwParam1,
-    DWORD_PTR dwParam2
-    )
-{
-    WaveVoiceIn *wave = (WaveVoiceIn *) dwInstance;
-
-    switch (msg) {
-    case WIM_DATA:
-        {
-            WAVEHDR *h = (WAVEHDR *) dwParam1;
-            if (!h->dwUser) {
-                h->dwUser = 1;
-                EnterCriticalSection (&wave->crit_sect);
-                {
-                    wave->avail += conf.adc_samples;
-                }
-                LeaveCriticalSection (&wave->crit_sect);
-                if (wave->hw.poll_mode) {
-                    if (!SetEvent (wave->event)) {
-                        dolog ("ADC SetEvent failed %lx\n", GetLastError ());
-                    }
-                }
-            }
-        }
-        break;
-
-    case WIM_CLOSE:
-    case WIM_OPEN:
-        break;
-
-    default:
-        dolog ("unknown wave in callback msg %x\n", msg);
-    }
-}
-
-static void winwave_add_buffers (WaveVoiceIn *wave, int samples)
-{
-    int doreset;
-
-    doreset = wave->hw.poll_mode && (samples >= conf.adc_samples);
-    if (doreset && !ResetEvent (wave->event)) {
-        dolog ("ADC ResetEvent failed %lx\n", GetLastError ());
-    }
-
-    while (samples >= conf.adc_samples) {
-        MMRESULT mr;
-        WAVEHDR *h = &wave->hdrs[wave->curhdr];
-
-        h->dwUser = 0;
-        mr = waveInAddBuffer (wave->hwi, h, sizeof (*h));
-        if (mr != MMSYSERR_NOERROR) {
-            winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr);
-        }
-        wave->curhdr = (wave->curhdr + 1) % conf.adc_headers;
-        samples -= conf.adc_samples;
-    }
-}
-
-static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as)
-{
-    int i;
-    int err;
-    MMRESULT mr;
-    WAVEFORMATEX wfx;
-    WaveVoiceIn *wave;
-
-    wave = (WaveVoiceIn *) hw;
-
-    InitializeCriticalSection (&wave->crit_sect);
-
-    err = waveformat_from_audio_settings (&wfx, as);
-    if (err) {
-        goto err0;
-    }
-
-    mr = waveInOpen (&wave->hwi, WAVE_MAPPER, &wfx,
-                     (DWORD_PTR) winwave_callback_in,
-                     (DWORD_PTR) wave, CALLBACK_FUNCTION);
-    if (mr != MMSYSERR_NOERROR) {
-        winwave_logerr (mr, "waveInOpen");
-        goto err1;
-    }
-
-    wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
-                               sizeof (*wave->hdrs));
-    if (!wave->hdrs) {
-        goto err2;
-    }
-
-    audio_pcm_init_info (&hw->info, as);
-    hw->samples = conf.adc_samples * conf.adc_headers;
-    wave->avail = 0;
-
-    wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.adc_samples,
-                                  conf.adc_headers << hw->info.shift);
-    if (!wave->pcm_buf) {
-        goto err3;
-    }
-
-    for (i = 0; i < conf.adc_headers; ++i) {
-        WAVEHDR *h = &wave->hdrs[i];
-
-        h->dwUser = 0;
-        h->dwBufferLength = conf.adc_samples << hw->info.shift;
-        h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
-        h->dwFlags = 0;
-
-        mr = waveInPrepareHeader (wave->hwi, h, sizeof (*h));
-        if (mr != MMSYSERR_NOERROR) {
-            winwave_logerr (mr, "waveInPrepareHeader(%d)", i);
-            goto err4;
-        }
-    }
-
-    wave->paused = 1;
-    winwave_add_buffers (wave, hw->samples);
-    return 0;
-
- err4:
-    g_free (wave->pcm_buf);
- err3:
-    g_free (wave->hdrs);
- err2:
-    winwave_anal_close_in (wave);
- err1:
- err0:
-    return -1;
-}
-
-static void winwave_fini_in (HWVoiceIn *hw)
-{
-    int i;
-    MMRESULT mr;
-    WaveVoiceIn *wave = (WaveVoiceIn *) hw;
-
-    mr = waveInReset (wave->hwi);
-    if (mr != MMSYSERR_NOERROR) {
-        winwave_logerr (mr, "waveInReset");
-    }
-
-    for (i = 0; i < conf.adc_headers; ++i) {
-        mr = waveInUnprepareHeader (wave->hwi, &wave->hdrs[i],
-                                     sizeof (wave->hdrs[i]));
-        if (mr != MMSYSERR_NOERROR) {
-            winwave_logerr (mr, "waveInUnprepareHeader(%d)", i);
-        }
-    }
-
-    winwave_anal_close_in (wave);
-
-    if (wave->event) {
-        qemu_del_wait_object (wave->event, winwave_poll, wave);
-        if (!CloseHandle (wave->event)) {
-            dolog ("ADC CloseHandle failed %lx\n", GetLastError ());
-        }
-        wave->event = NULL;
-    }
-
-    g_free (wave->pcm_buf);
-    wave->pcm_buf = NULL;
-
-    g_free (wave->hdrs);
-    wave->hdrs = NULL;
-}
-
-static int winwave_run_in (HWVoiceIn *hw)
-{
-    WaveVoiceIn *wave = (WaveVoiceIn *) hw;
-    int live = audio_pcm_hw_get_live_in (hw);
-    int dead = hw->samples - live;
-    int decr, ret;
-
-    if (!dead) {
-        return 0;
-    }
-
-    EnterCriticalSection (&wave->crit_sect);
-    {
-        decr = audio_MIN (dead, wave->avail);
-        wave->avail -= decr;
-    }
-    LeaveCriticalSection (&wave->crit_sect);
-
-    ret = decr;
-    while (decr) {
-        int left = hw->samples - hw->wpos;
-        int conv = audio_MIN (left, decr);
-        hw->conv (hw->conv_buf + hw->wpos,
-                  advance (wave->pcm_buf, wave->rpos << hw->info.shift),
-                  conv);
-
-        wave->rpos = (wave->rpos + conv) % hw->samples;
-        hw->wpos = (hw->wpos + conv) % hw->samples;
-        decr -= conv;
-    }
-
-    winwave_add_buffers (wave, ret);
-    return ret;
-}
-
-static int winwave_read (SWVoiceIn *sw, void *buf, int size)
-{
-    return audio_pcm_sw_read (sw, buf, size);
-}
-
-static int winwave_ctl_in (HWVoiceIn *hw, int cmd, ...)
-{
-    MMRESULT mr;
-    WaveVoiceIn *wave = (WaveVoiceIn *) hw;
-
-    switch (cmd) {
-    case VOICE_ENABLE:
-        {
-            va_list ap;
-            int poll_mode;
-
-            va_start (ap, cmd);
-            poll_mode = va_arg (ap, int);
-            va_end (ap);
-
-            if (poll_mode && !wave->event) {
-                wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
-                if (!wave->event) {
-                    dolog ("ADC CreateEvent: %lx, poll mode will be disabled\n",
-                           GetLastError ());
-                }
-            }
-
-            if (wave->event) {
-                int ret;
-
-                ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
-                hw->poll_mode = (ret == 0);
-            }
-            else {
-                hw->poll_mode = 0;
-            }
-            if (wave->paused) {
-                mr = waveInStart (wave->hwi);
-                if (mr != MMSYSERR_NOERROR) {
-                    winwave_logerr (mr, "waveInStart");
-                }
-                wave->paused = 0;
-            }
-        }
-        return 0;
-
-    case VOICE_DISABLE:
-        if (!wave->paused) {
-            mr = waveInStop (wave->hwi);
-            if (mr != MMSYSERR_NOERROR) {
-                winwave_logerr (mr, "waveInStop");
-            }
-            else {
-                wave->paused = 1;
-            }
-        }
-        if (wave->event) {
-            qemu_del_wait_object (wave->event, winwave_poll, wave);
-        }
-        return 0;
-    }
-    return 0;
-}
-
-static void *winwave_audio_init (void)
-{
-    return &conf;
-}
-
-static void winwave_audio_fini (void *opaque)
-{
-    (void) opaque;
-}
-
-static struct audio_option winwave_options[] = {
-    {
-        .name        = "DAC_HEADERS",
-        .tag         = AUD_OPT_INT,
-        .valp        = &conf.dac_headers,
-        .descr       = "DAC number of headers",
-    },
-    {
-        .name        = "DAC_SAMPLES",
-        .tag         = AUD_OPT_INT,
-        .valp        = &conf.dac_samples,
-        .descr       = "DAC number of samples per header",
-    },
-    {
-        .name        = "ADC_HEADERS",
-        .tag         = AUD_OPT_INT,
-        .valp        = &conf.adc_headers,
-        .descr       = "ADC number of headers",
-    },
-    {
-        .name        = "ADC_SAMPLES",
-        .tag         = AUD_OPT_INT,
-        .valp        = &conf.adc_samples,
-        .descr       = "ADC number of samples per header",
-    },
-    { /* End of list */ }
-};
-
-static struct audio_pcm_ops winwave_pcm_ops = {
-    .init_out = winwave_init_out,
-    .fini_out = winwave_fini_out,
-    .run_out  = winwave_run_out,
-    .write    = winwave_write,
-    .ctl_out  = winwave_ctl_out,
-    .init_in  = winwave_init_in,
-    .fini_in  = winwave_fini_in,
-    .run_in   = winwave_run_in,
-    .read     = winwave_read,
-    .ctl_in   = winwave_ctl_in
-};
-
-struct audio_driver winwave_audio_driver = {
-    .name           = "winwave",
-    .descr          = "Windows Waveform Audio http://msdn.microsoft.com",
-    .options        = winwave_options,
-    .init           = winwave_audio_init,
-    .fini           = winwave_audio_fini,
-    .pcm_ops        = &winwave_pcm_ops,
-    .can_be_default = 1,
-    .max_voices_out = INT_MAX,
-    .max_voices_in  = INT_MAX,
-    .voice_size_out = sizeof (WaveVoiceOut),
-    .voice_size_in  = sizeof (WaveVoiceIn)
-};
diff --git a/block.c b/block.c
index 2b9ceae02f..dd4f58d6cb 100644
--- a/block.c
+++ b/block.c
@@ -36,6 +36,7 @@
 #include "qmp-commands.h"
 #include "qemu/timer.h"
 #include "qapi-event.h"
+#include "block/throttle-groups.h"
 
 #ifdef CONFIG_BSD
 #include <sys/types.h>
@@ -79,6 +80,12 @@ static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states =
 static QLIST_HEAD(, BlockDriver) bdrv_drivers =
     QLIST_HEAD_INITIALIZER(bdrv_drivers);
 
+static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
+                             const char *reference, QDict *options, int flags,
+                             BlockDriverState *parent,
+                             const BdrvChildRole *child_role,
+                             BlockDriver *drv, Error **errp);
+
 static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs);
 /* If non-zero, use only whitelisted block drivers */
 static int use_bdrv_whitelist;
@@ -682,8 +689,8 @@ static int bdrv_temp_snapshot_flags(int flags)
 }
 
 /*
- * Returns the flags that bs->file should get, based on the given flags for
- * the parent BDS
+ * Returns the flags that bs->file should get if a protocol driver is expected,
+ * based on the given flags for the parent BDS
  */
 static int bdrv_inherited_flags(int flags)
 {
@@ -700,6 +707,25 @@ static int bdrv_inherited_flags(int flags)
     return flags;
 }
 
+const BdrvChildRole child_file = {
+    .inherit_flags = bdrv_inherited_flags,
+};
+
+/*
+ * Returns the flags that bs->file should get if the use of formats (and not
+ * only protocols) is permitted for it, based on the given flags for the parent
+ * BDS
+ */
+static int bdrv_inherited_fmt_flags(int parent_flags)
+{
+    int flags = child_file.inherit_flags(parent_flags);
+    return flags & ~BDRV_O_PROTOCOL;
+}
+
+const BdrvChildRole child_format = {
+    .inherit_flags = bdrv_inherited_fmt_flags,
+};
+
 /*
  * Returns the flags that bs->backing_hd should get, based on the given flags
  * for the parent BDS
@@ -715,6 +741,10 @@ static int bdrv_backing_flags(int flags)
     return flags;
 }
 
+static const BdrvChildRole child_backing = {
+    .inherit_flags = bdrv_backing_flags,
+};
+
 static int bdrv_open_flags(BlockDriverState *bs, int flags)
 {
     int open_flags = flags | BDRV_O_CACHE_WB;
@@ -767,6 +797,19 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
     QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list);
 }
 
+static QemuOptsList bdrv_runtime_opts = {
+    .name = "bdrv_common",
+    .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head),
+    .desc = {
+        {
+            .name = "node-name",
+            .type = QEMU_OPT_STRING,
+            .help = "Node name of the block device node",
+        },
+        { /* end of list */ }
+    },
+};
+
 /*
  * Common part for opening disk images and files
  *
@@ -778,6 +821,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
     int ret, open_flags;
     const char *filename;
     const char *node_name = NULL;
+    QemuOpts *opts;
     Error *local_err = NULL;
 
     assert(drv != NULL);
@@ -798,23 +842,22 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
 
     trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
 
-    node_name = qdict_get_try_str(options, "node-name");
-    bdrv_assign_node_name(bs, node_name, &local_err);
+    opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
+    qemu_opts_absorb_qdict(opts, options, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
-        return -EINVAL;
+        ret = -EINVAL;
+        goto fail_opts;
     }
-    qdict_del(options, "node-name");
 
-    /* bdrv_open() with directly using a protocol as drv. This layer is already
-     * opened, so assign it to bs (while file becomes a closed BlockDriverState)
-     * and return immediately. */
-    if (file != NULL && drv->bdrv_file_open) {
-        bdrv_swap(file, bs);
-        return 0;
+    node_name = qemu_opt_get(opts, "node-name");
+    bdrv_assign_node_name(bs, node_name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        ret = -EINVAL;
+        goto fail_opts;
     }
 
-    bs->open_flags = flags;
     bs->guest_block_size = 512;
     bs->request_alignment = 512;
     bs->zero_beyond_eof = true;
@@ -827,7 +870,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
                         ? "Driver '%s' can only be used for read-only devices"
                         : "Driver '%s' is not whitelisted",
                    drv->format_name);
-        return -ENOTSUP;
+        ret = -ENOTSUP;
+        goto fail_opts;
     }
 
     assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
@@ -836,7 +880,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
             bdrv_enable_copy_on_read(bs);
         } else {
             error_setg(errp, "Can't use copy-on-read on read-only device");
-            return -EINVAL;
+            ret = -EINVAL;
+            goto fail_opts;
         }
     }
 
@@ -902,6 +947,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
     assert(bdrv_opt_mem_align(bs) != 0);
     assert(bdrv_min_mem_align(bs) != 0);
     assert((bs->request_alignment != 0) || bs->sg);
+
+    qemu_opts_del(opts);
     return 0;
 
 free_and_fail:
@@ -909,6 +956,8 @@ free_and_fail:
     g_free(bs->opaque);
     bs->opaque = NULL;
     bs->drv = NULL;
+fail_opts:
+    qemu_opts_del(opts);
     return ret;
 }
 
@@ -942,14 +991,17 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
 /*
  * Fills in default options for opening images and converts the legacy
  * filename/flags pair to option QDict entries.
+ * The BDRV_O_PROTOCOL flag in *flags will be set or cleared accordingly if a
+ * block driver has been specified explicitly.
  */
-static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
-                             BlockDriver *drv, Error **errp)
+static int bdrv_fill_options(QDict **options, const char **pfilename,
+                             int *flags, BlockDriver *drv, Error **errp)
 {
     const char *filename = *pfilename;
     const char *drvname;
-    bool protocol = flags & BDRV_O_PROTOCOL;
+    bool protocol = *flags & BDRV_O_PROTOCOL;
     bool parse_filename = false;
+    BlockDriver *tmp_drv;
     Error *local_err = NULL;
 
     /* Parse json: pseudo-protocol */
@@ -967,6 +1019,24 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
         *pfilename = filename = NULL;
     }
 
+    drvname = qdict_get_try_str(*options, "driver");
+
+    /* If the user has explicitly specified the driver, this choice should
+     * override the BDRV_O_PROTOCOL flag */
+    tmp_drv = drv;
+    if (!tmp_drv && drvname) {
+        tmp_drv = bdrv_find_format(drvname);
+    }
+    if (tmp_drv) {
+        protocol = tmp_drv->bdrv_file_open;
+    }
+
+    if (protocol) {
+        *flags |= BDRV_O_PROTOCOL;
+    } else {
+        *flags &= ~BDRV_O_PROTOCOL;
+    }
+
     /* Fetch the file name from the options QDict if necessary */
     if (protocol && filename) {
         if (!qdict_haskey(*options, "filename")) {
@@ -981,7 +1051,6 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
 
     /* Find the right block driver */
     filename = qdict_get_try_str(*options, "filename");
-    drvname = qdict_get_try_str(*options, "driver");
 
     if (drv) {
         if (drvname) {
@@ -1118,9 +1187,10 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
     }
 
     assert(bs->backing_hd == NULL);
-    ret = bdrv_open(&backing_hd,
-                    *backing_filename ? backing_filename : NULL, NULL, options,
-                    bdrv_backing_flags(bs->open_flags), NULL, &local_err);
+    ret = bdrv_open_inherit(&backing_hd,
+                            *backing_filename ? backing_filename : NULL,
+                            NULL, options, 0, bs, &child_backing,
+                            NULL, &local_err);
     if (ret < 0) {
         bdrv_unref(backing_hd);
         backing_hd = NULL;
@@ -1154,7 +1224,8 @@ free_exit:
  * To conform with the behavior of bdrv_open(), *pbs has to be NULL.
  */
 int bdrv_open_image(BlockDriverState **pbs, const char *filename,
-                    QDict *options, const char *bdref_key, int flags,
+                    QDict *options, const char *bdref_key,
+                    BlockDriverState* parent, const BdrvChildRole *child_role,
                     bool allow_none, Error **errp)
 {
     QDict *image_options;
@@ -1182,7 +1253,8 @@ int bdrv_open_image(BlockDriverState **pbs, const char *filename,
         goto done;
     }
 
-    ret = bdrv_open(pbs, filename, reference, image_options, flags, NULL, errp);
+    ret = bdrv_open_inherit(pbs, filename, reference, image_options, 0,
+                            parent, child_role, NULL, errp);
 
 done:
     qdict_del(options, bdref_key);
@@ -1254,6 +1326,19 @@ out:
     return ret;
 }
 
+static void bdrv_attach_child(BlockDriverState *parent_bs,
+                              BlockDriverState *child_bs,
+                              const BdrvChildRole *child_role)
+{
+    BdrvChild *child = g_new(BdrvChild, 1);
+    *child = (BdrvChild) {
+        .bs     = child_bs,
+        .role   = child_role,
+    };
+
+    QLIST_INSERT_HEAD(&parent_bs->children, child, next);
+}
+
 /*
  * Opens a disk image (raw, qcow2, vmdk, ...)
  *
@@ -1269,9 +1354,11 @@ out:
  * should be opened. If specified, neither options nor a filename may be given,
  * nor can an existing BDS be reused (that is, *pbs has to be NULL).
  */
-int bdrv_open(BlockDriverState **pbs, const char *filename,
-              const char *reference, QDict *options, int flags,
-              BlockDriver *drv, Error **errp)
+static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
+                             const char *reference, QDict *options, int flags,
+                             BlockDriverState *parent,
+                             const BdrvChildRole *child_role,
+                             BlockDriver *drv, Error **errp)
 {
     int ret;
     BlockDriverState *file = NULL, *bs;
@@ -1280,6 +1367,8 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
     int snapshot_flags = 0;
 
     assert(pbs);
+    assert(!child_role || !flags);
+    assert(!child_role == !parent);
 
     if (reference) {
         bool options_non_empty = options ? qdict_size(options) : false;
@@ -1302,6 +1391,9 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
             return -ENODEV;
         }
         bdrv_ref(bs);
+        if (child_role) {
+            bdrv_attach_child(parent, bs, child_role);
+        }
         *pbs = bs;
         return 0;
     }
@@ -1317,7 +1409,12 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
         options = qdict_new();
     }
 
-    ret = bdrv_fill_options(&options, &filename, flags, drv, &local_err);
+    if (child_role) {
+        bs->inherits_from = parent;
+        flags = child_role->inherit_flags(parent->open_flags);
+    }
+
+    ret = bdrv_fill_options(&options, &filename, &flags, drv, &local_err);
     if (local_err) {
         goto fail;
     }
@@ -1336,12 +1433,8 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
     }
 
     assert(drvname || !(flags & BDRV_O_PROTOCOL));
-    if (drv && !drv->bdrv_file_open) {
-        /* If the user explicitly wants a format driver here, we'll need to add
-         * another layer for the protocol in bs->file */
-        flags &= ~BDRV_O_PROTOCOL;
-    }
 
+    bs->open_flags = flags;
     bs->options = options;
     options = qdict_clone_shallow(options);
 
@@ -1356,9 +1449,9 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
         }
 
         assert(file == NULL);
+        bs->open_flags = flags;
         ret = bdrv_open_image(&file, filename, options, "file",
-                              bdrv_inherited_flags(flags),
-                              true, &local_err);
+                              bs, &child_file, true, &local_err);
         if (ret < 0) {
             goto fail;
         }
@@ -1377,6 +1470,12 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
         goto fail;
     }
 
+    /* BDRV_O_PROTOCOL must be set iff a protocol BDS is about to be created */
+    assert(!!(flags & BDRV_O_PROTOCOL) == !!drv->bdrv_file_open);
+    /* file must be NULL if a protocol BDS is about to be created
+     * (the inverse results in an error message from bdrv_open_common()) */
+    assert(!(flags & BDRV_O_PROTOCOL) || !file);
+
     /* Open the image */
     ret = bdrv_open_common(bs, file, options, flags, drv, &local_err);
     if (ret < 0) {
@@ -1439,6 +1538,10 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
         goto close_and_fail;
     }
 
+    if (child_role) {
+        bdrv_attach_child(parent, bs, child_role);
+    }
+
     QDECREF(options);
     *pbs = bs;
     return 0;
@@ -1475,6 +1578,14 @@ close_and_fail:
     return ret;
 }
 
+int bdrv_open(BlockDriverState **pbs, const char *filename,
+              const char *reference, QDict *options, int flags,
+              BlockDriver *drv, Error **errp)
+{
+    return bdrv_open_inherit(pbs, filename, reference, options, flags, NULL,
+                             NULL, drv, errp);
+}
+
 typedef struct BlockReopenQueueEntry {
      bool prepared;
      BDRVReopenState state;
@@ -1505,6 +1616,8 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
     assert(bs != NULL);
 
     BlockReopenQueueEntry *bs_entry;
+    BdrvChild *child;
+
     if (bs_queue == NULL) {
         bs_queue = g_new0(BlockReopenQueue, 1);
         QSIMPLEQ_INIT(bs_queue);
@@ -1513,8 +1626,15 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
     /* bdrv_open() masks this flag out */
     flags &= ~BDRV_O_PROTOCOL;
 
-    if (bs->file) {
-        bdrv_reopen_queue(bs_queue, bs->file, bdrv_inherited_flags(flags));
+    QLIST_FOREACH(child, &bs->children, next) {
+        int child_flags;
+
+        if (child->bs->inherits_from != bs) {
+            continue;
+        }
+
+        child_flags = child->role->inherit_flags(flags);
+        bdrv_reopen_queue(bs_queue, child->bs, child_flags);
     }
 
     bs_entry = g_new0(BlockReopenQueueEntry, 1);
@@ -1725,6 +1845,16 @@ void bdrv_close(BlockDriverState *bs)
     notifier_list_notify(&bs->close_notifiers, bs);
 
     if (bs->drv) {
+        BdrvChild *child, *next;
+
+        QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
+            if (child->bs->inherits_from == bs) {
+                child->bs->inherits_from = NULL;
+            }
+            QLIST_REMOVE(child, next);
+            g_free(child);
+        }
+
         if (bs->backing_hd) {
             BlockDriverState *backing_hd = bs->backing_hd;
             bdrv_set_backing_hd(bs, NULL);
@@ -1822,12 +1952,18 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
     bs_dest->enable_write_cache = bs_src->enable_write_cache;
 
     /* i/o throttled req */
-    memcpy(&bs_dest->throttle_state,
-           &bs_src->throttle_state,
-           sizeof(ThrottleState));
+    bs_dest->throttle_state     = bs_src->throttle_state,
+    bs_dest->io_limits_enabled  = bs_src->io_limits_enabled;
+    bs_dest->pending_reqs[0]    = bs_src->pending_reqs[0];
+    bs_dest->pending_reqs[1]    = bs_src->pending_reqs[1];
     bs_dest->throttled_reqs[0]  = bs_src->throttled_reqs[0];
     bs_dest->throttled_reqs[1]  = bs_src->throttled_reqs[1];
-    bs_dest->io_limits_enabled  = bs_src->io_limits_enabled;
+    memcpy(&bs_dest->round_robin,
+           &bs_src->round_robin,
+           sizeof(bs_dest->round_robin));
+    memcpy(&bs_dest->throttle_timers,
+           &bs_src->throttle_timers,
+           sizeof(ThrottleTimers));
 
     /* r/w error */
     bs_dest->on_read_error      = bs_src->on_read_error;
@@ -1869,6 +2005,10 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
 void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
 {
     BlockDriverState tmp;
+    BdrvChild *child;
+
+    bdrv_drain(bs_new);
+    bdrv_drain(bs_old);
 
     /* The code needs to swap the node_name but simply swapping node_list won't
      * work so first remove the nodes from the graph list, do the swap then
@@ -1881,12 +2021,21 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
         QTAILQ_REMOVE(&graph_bdrv_states, bs_old, node_list);
     }
 
+    /* If the BlockDriverState is part of a throttling group acquire
+     * its lock since we're going to mess with the protected fields.
+     * Otherwise there's no need to worry since no one else can touch
+     * them. */
+    if (bs_old->throttle_state) {
+        throttle_group_lock(bs_old);
+    }
+
     /* bs_new must be unattached and shouldn't have anything fancy enabled */
     assert(!bs_new->blk);
     assert(QLIST_EMPTY(&bs_new->dirty_bitmaps));
     assert(bs_new->job == NULL);
     assert(bs_new->io_limits_enabled == false);
-    assert(!throttle_have_timer(&bs_new->throttle_state));
+    assert(bs_new->throttle_state == NULL);
+    assert(!throttle_timers_are_initialized(&bs_new->throttle_timers));
 
     tmp = *bs_new;
     *bs_new = *bs_old;
@@ -1903,7 +2052,13 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
     /* Check a few fields that should remain attached to the device */
     assert(bs_new->job == NULL);
     assert(bs_new->io_limits_enabled == false);
-    assert(!throttle_have_timer(&bs_new->throttle_state));
+    assert(bs_new->throttle_state == NULL);
+    assert(!throttle_timers_are_initialized(&bs_new->throttle_timers));
+
+    /* Release the ThrottleGroup lock */
+    if (bs_old->throttle_state) {
+        throttle_group_unlock(bs_old);
+    }
 
     /* insert the nodes back into the graph node list if needed */
     if (bs_new->node_name[0] != '\0') {
@@ -1913,6 +2068,30 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
         QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs_old, node_list);
     }
 
+    /*
+     * Update lh_first.le_prev for non-empty lists.
+     *
+     * The head of the op blocker list doesn't change because it is moved back
+     * in bdrv_move_feature_fields().
+     */
+    assert(QLIST_EMPTY(&bs_old->tracked_requests));
+    assert(QLIST_EMPTY(&bs_new->tracked_requests));
+
+    QLIST_FIX_HEAD_PTR(&bs_new->children, next);
+    QLIST_FIX_HEAD_PTR(&bs_old->children, next);
+
+    /* Update references in bs->opaque and children */
+    QLIST_FOREACH(child, &bs_old->children, next) {
+        if (child->bs->inherits_from == bs_new) {
+            child->bs->inherits_from = bs_old;
+        }
+    }
+    QLIST_FOREACH(child, &bs_new->children, next) {
+        if (child->bs->inherits_from == bs_old) {
+            child->bs->inherits_from = bs_new;
+        }
+    }
+
     bdrv_rebind(bs_new);
     bdrv_rebind(bs_old);
 }
@@ -1935,6 +2114,7 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
     /* The contents of 'tmp' will become bs_top, as we are
      * swapping bs_new and bs_top contents. */
     bdrv_set_backing_hd(bs_top, bs_new);
+    bdrv_attach_child(bs_top, bs_new, &child_backing);
 }
 
 static void bdrv_delete(BlockDriverState *bs)
@@ -3220,10 +3400,9 @@ static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
     uint64_t size = bdrv_nb_sectors(bs);
 
     QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
-        if (bdrv_dirty_bitmap_frozen(bitmap)) {
-            continue;
-        }
+        assert(!bdrv_dirty_bitmap_frozen(bitmap));
         hbitmap_truncate(bitmap->bitmap, size);
+        bitmap->size = size;
     }
 }
 
@@ -3691,7 +3870,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
     }
 
     if (bs->io_limits_enabled) {
-        throttle_detach_aio_context(&bs->throttle_state);
+        throttle_timers_detach_aio_context(&bs->throttle_timers);
     }
     if (bs->drv->bdrv_detach_aio_context) {
         bs->drv->bdrv_detach_aio_context(bs);
@@ -3727,7 +3906,7 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
         bs->drv->bdrv_attach_aio_context(bs, new_context);
     }
     if (bs->io_limits_enabled) {
-        throttle_attach_aio_context(&bs->throttle_state, new_context);
+        throttle_timers_attach_aio_context(&bs->throttle_timers, new_context);
     }
 
     QLIST_FOREACH(ban, &bs->aio_notifiers, list) {
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 0d8c2a4ab6..c34fd7cdc2 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -10,6 +10,7 @@ block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
 block-obj-$(CONFIG_POSIX) += raw-posix.o
 block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
 block-obj-y += null.o mirror.o io.o
+block-obj-y += throttle-groups.o
 
 block-obj-y += nbd.o nbd-client.o sheepdog.o
 block-obj-$(CONFIG_LIBISCSI) += iscsi.o
diff --git a/block/blkdebug.c b/block/blkdebug.c
index 1e92607ef3..bc247f46f5 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -429,7 +429,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
     /* Open the backing file */
     assert(bs->file == NULL);
     ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image",
-                          flags | BDRV_O_PROTOCOL, false, &local_err);
+                          bs, &child_file, false, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
         goto out;
diff --git a/block/blkverify.c b/block/blkverify.c
index 438dff8bcb..d277e63220 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -125,7 +125,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
     /* Open the raw file */
     assert(bs->file == NULL);
     ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options,
-                          "raw", flags | BDRV_O_PROTOCOL, false, &local_err);
+                          "raw", bs, &child_file, false, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
         goto fail;
@@ -134,7 +134,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
     /* Open the test file */
     assert(s->test_file == NULL);
     ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options,
-                          "test", flags, false, &local_err);
+                          "test", bs, &child_format, false, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
         s->test_file = NULL;
diff --git a/block/io.c b/block/io.c
index e394d92626..bb4f78784e 100644
--- a/block/io.c
+++ b/block/io.c
@@ -23,9 +23,9 @@
  */
 
 #include "trace.h"
-#include "sysemu/qtest.h"
 #include "block/blockjob.h"
 #include "block/block_int.h"
+#include "block/throttle-groups.h"
 
 #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
 
@@ -65,7 +65,7 @@ void bdrv_set_io_limits(BlockDriverState *bs,
 {
     int i;
 
-    throttle_config(&bs->throttle_state, cfg);
+    throttle_group_config(bs, cfg);
 
     for (i = 0; i < 2; i++) {
         qemu_co_enter_next(&bs->throttled_reqs[i]);
@@ -95,72 +95,33 @@ static bool bdrv_start_throttled_reqs(BlockDriverState *bs)
 void bdrv_io_limits_disable(BlockDriverState *bs)
 {
     bs->io_limits_enabled = false;
-
     bdrv_start_throttled_reqs(bs);
-
-    throttle_destroy(&bs->throttle_state);
-}
-
-static void bdrv_throttle_read_timer_cb(void *opaque)
-{
-    BlockDriverState *bs = opaque;
-    qemu_co_enter_next(&bs->throttled_reqs[0]);
-}
-
-static void bdrv_throttle_write_timer_cb(void *opaque)
-{
-    BlockDriverState *bs = opaque;
-    qemu_co_enter_next(&bs->throttled_reqs[1]);
+    throttle_group_unregister_bs(bs);
 }
 
 /* should be called before bdrv_set_io_limits if a limit is set */
-void bdrv_io_limits_enable(BlockDriverState *bs)
+void bdrv_io_limits_enable(BlockDriverState *bs, const char *group)
 {
-    int clock_type = QEMU_CLOCK_REALTIME;
-
-    if (qtest_enabled()) {
-        /* For testing block IO throttling only */
-        clock_type = QEMU_CLOCK_VIRTUAL;
-    }
     assert(!bs->io_limits_enabled);
-    throttle_init(&bs->throttle_state,
-                  bdrv_get_aio_context(bs),
-                  clock_type,
-                  bdrv_throttle_read_timer_cb,
-                  bdrv_throttle_write_timer_cb,
-                  bs);
+    throttle_group_register_bs(bs, group);
     bs->io_limits_enabled = true;
 }
 
-/* This function makes an IO wait if needed
- *
- * @nb_sectors: the number of sectors of the IO
- * @is_write:   is the IO a write
- */
-static void bdrv_io_limits_intercept(BlockDriverState *bs,
-                                     unsigned int bytes,
-                                     bool is_write)
+void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group)
 {
-    /* does this io must wait */
-    bool must_wait = throttle_schedule_timer(&bs->throttle_state, is_write);
-
-    /* if must wait or any request of this type throttled queue the IO */
-    if (must_wait ||
-        !qemu_co_queue_empty(&bs->throttled_reqs[is_write])) {
-        qemu_co_queue_wait(&bs->throttled_reqs[is_write]);
+    /* this bs is not part of any group */
+    if (!bs->throttle_state) {
+        return;
     }
 
-    /* the IO will be executed, do the accounting */
-    throttle_account(&bs->throttle_state, is_write, bytes);
-
-
-    /* if the next request must wait -> do nothing */
-    if (throttle_schedule_timer(&bs->throttle_state, is_write)) {
+    /* this bs is a part of the same group than the one we want */
+    if (!g_strcmp0(throttle_group_get_name(bs), group)) {
         return;
     }
 
-    /* else queue next request for execution */
-    qemu_co_queue_next(&bs->throttled_reqs[is_write]);
+    /* need to change the group this bs belong to */
+    bdrv_io_limits_disable(bs);
+    bdrv_io_limits_enable(bs, group);
 }
 
 void bdrv_setup_io_funcs(BlockDriver *bdrv)
@@ -967,7 +928,7 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs,
 
     /* throttling disk I/O */
     if (bs->io_limits_enabled) {
-        bdrv_io_limits_intercept(bs, bytes, false);
+        throttle_group_co_io_limits_intercept(bs, bytes, false);
     }
 
     /* Align read if necessary by padding qiov */
@@ -1297,7 +1258,7 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
 
     /* throttling disk I/O */
     if (bs->io_limits_enabled) {
-        bdrv_io_limits_intercept(bs, bytes, true);
+        throttle_group_co_io_limits_intercept(bs, bytes, true);
     }
 
     /*
diff --git a/block/qapi.c b/block/qapi.c
index 18d2b95f54..a738148bce 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -24,6 +24,7 @@
 
 #include "block/qapi.h"
 #include "block/block_int.h"
+#include "block/throttle-groups.h"
 #include "block/write-threshold.h"
 #include "qmp-commands.h"
 #include "qapi-visit.h"
@@ -65,7 +66,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
 
     if (bs->io_limits_enabled) {
         ThrottleConfig cfg;
-        throttle_get_config(&bs->throttle_state, &cfg);
+
+        throttle_group_get_config(bs, &cfg);
+
         info->bps     = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
         info->bps_rd  = cfg.buckets[THROTTLE_BPS_READ].avg;
         info->bps_wr  = cfg.buckets[THROTTLE_BPS_WRITE].avg;
@@ -90,6 +93,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
 
         info->has_iops_size = cfg.op_size;
         info->iops_size = cfg.op_size;
+
+        info->has_group = true;
+        info->group = g_strdup(throttle_group_get_name(bs));
     }
 
     info->write_threshold = bdrv_write_threshold_get(bs);
diff --git a/block/qcow2.c b/block/qcow2.c
index f7b4cc6a32..c4f6938a36 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -483,9 +483,11 @@ static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = {
     [QCOW2_OL_INACTIVE_L2_BITNR]    = QCOW2_OPT_OVERLAP_INACTIVE_L2,
 };
 
-static void read_cache_sizes(QemuOpts *opts, uint64_t *l2_cache_size,
+static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
+                             uint64_t *l2_cache_size,
                              uint64_t *refcount_cache_size, Error **errp)
 {
+    BDRVQcowState *s = bs->opaque;
     uint64_t combined_cache_size;
     bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set;
 
@@ -525,7 +527,9 @@ static void read_cache_sizes(QemuOpts *opts, uint64_t *l2_cache_size,
         }
     } else {
         if (!l2_cache_size_set && !refcount_cache_size_set) {
-            *l2_cache_size = DEFAULT_L2_CACHE_BYTE_SIZE;
+            *l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE,
+                                 (uint64_t)DEFAULT_L2_CACHE_CLUSTERS
+                                 * s->cluster_size);
             *refcount_cache_size = *l2_cache_size
                                  / DEFAULT_L2_REFCOUNT_SIZE_RATIO;
         } else if (!l2_cache_size_set) {
@@ -803,7 +807,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
         goto fail;
     }
 
-    read_cache_sizes(opts, &l2_cache_size, &refcount_cache_size, &local_err);
+    read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size,
+                     &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         ret = -EINVAL;
diff --git a/block/qcow2.h b/block/qcow2.h
index 0076512af4..5936d299a3 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -62,11 +62,14 @@
 #define MIN_CLUSTER_BITS 9
 #define MAX_CLUSTER_BITS 21
 
-#define MIN_L2_CACHE_SIZE 1 /* cluster */
+/* Must be at least 2 to cover COW */
+#define MIN_L2_CACHE_SIZE 2 /* clusters */
 
 /* Must be at least 4 to cover all cases of refcount table growth */
 #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
 
+/* Whichever is more */
+#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */
 #define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */
 
 /* The refblock cache needs only a fourth of the L2 cache size to cover as many
diff --git a/block/quorum.c b/block/quorum.c
index f91ef75a84..77e55b2775 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -866,25 +866,18 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
     Error *local_err = NULL;
     QemuOpts *opts = NULL;
     bool *opened;
-    QDict *sub = NULL;
-    QList *list = NULL;
-    const QListEntry *lentry;
     int i;
     int ret = 0;
 
     qdict_flatten(options);
-    qdict_extract_subqdict(options, &sub, "children.");
-    qdict_array_split(sub, &list);
 
-    if (qdict_size(sub)) {
-        error_setg(&local_err, "Invalid option children.%s",
-                   qdict_first(sub)->key);
+    /* count how many different children are present */
+    s->num_children = qdict_array_entries(options, "children.");
+    if (s->num_children < 0) {
+        error_setg(&local_err, "Option children is not a valid array");
         ret = -EINVAL;
         goto exit;
     }
-
-    /* count how many different children are present */
-    s->num_children = qlist_size(list);
     if (s->num_children < 2) {
         error_setg(&local_err,
                    "Number of provided children must be greater than 1");
@@ -937,37 +930,17 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
     s->bs = g_new0(BlockDriverState *, s->num_children);
     opened = g_new0(bool, s->num_children);
 
-    for (i = 0, lentry = qlist_first(list); lentry;
-         lentry = qlist_next(lentry), i++) {
-        QDict *d;
-        QString *string;
-
-        switch (qobject_type(lentry->value))
-        {
-            /* List of options */
-            case QTYPE_QDICT:
-                d = qobject_to_qdict(lentry->value);
-                QINCREF(d);
-                ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL,
-                                &local_err);
-                break;
-
-            /* QMP reference */
-            case QTYPE_QSTRING:
-                string = qobject_to_qstring(lentry->value);
-                ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL,
-                                flags, NULL, &local_err);
-                break;
-
-            default:
-                error_setg(&local_err, "Specification of child block device %i "
-                           "is invalid", i);
-                ret = -EINVAL;
-        }
+    for (i = 0; i < s->num_children; i++) {
+        char indexstr[32];
+        ret = snprintf(indexstr, 32, "children.%d", i);
+        assert(ret < 32);
 
+        ret = bdrv_open_image(&s->bs[i], NULL, options, indexstr, bs,
+                              &child_format, false, &local_err);
         if (ret < 0) {
             goto close_exit;
         }
+
         opened[i] = true;
     }
 
@@ -990,8 +963,6 @@ exit:
     if (local_err) {
         error_propagate(errp, local_err);
     }
-    QDECREF(list);
-    QDECREF(sub);
     return ret;
 }
 
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 2990e954ae..44ade8cf4e 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -1848,8 +1848,9 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
         *pnum = nb_sectors;
         ret = BDRV_BLOCK_DATA;
     } else if (data == start) {
-        /* On a data extent, compute sectors to the end of the extent.  */
-        *pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE);
+        /* On a data extent, compute sectors to the end of the extent,
+         * possibly including a partial sector at EOF. */
+        *pnum = MIN(nb_sectors, DIV_ROUND_UP(hole - start, BDRV_SECTOR_SIZE));
         ret = BDRV_BLOCK_DATA;
     } else {
         /* On a hole, compute sectors to the beginning of the next extent.  */
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
new file mode 100644
index 0000000000..efc462fbc5
--- /dev/null
+++ b/block/throttle-groups.c
@@ -0,0 +1,496 @@
+/*
+ * QEMU block throttling group infrastructure
+ *
+ * Copyright (C) Nodalink, EURL. 2014
+ * Copyright (C) Igalia, S.L. 2015
+ *
+ * Authors:
+ *   Benoît Canet <benoit.canet@nodalink.com>
+ *   Alberto Garcia <berto@igalia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "block/throttle-groups.h"
+#include "qemu/queue.h"
+#include "qemu/thread.h"
+#include "sysemu/qtest.h"
+
+/* The ThrottleGroup structure (with its ThrottleState) is shared
+ * among different BlockDriverState and it's independent from
+ * AioContext, so in order to use it from different threads it needs
+ * its own locking.
+ *
+ * This locking is however handled internally in this file, so it's
+ * mostly transparent to outside users (but see the documentation in
+ * throttle_groups_lock()).
+ *
+ * The whole ThrottleGroup structure is private and invisible to
+ * outside users, that only use it through its ThrottleState.
+ *
+ * In addition to the ThrottleGroup structure, BlockDriverState has
+ * fields that need to be accessed by other members of the group and
+ * therefore also need to be protected by this lock. Once a BDS is
+ * registered in a group those fields can be accessed by other threads
+ * any time.
+ *
+ * Again, all this is handled internally and is mostly transparent to
+ * the outside. The 'throttle_timers' field however has an additional
+ * constraint because it may be temporarily invalid (see for example
+ * bdrv_set_aio_context()). Therefore in this file a thread will
+ * access some other BDS's timers only after verifying that that BDS
+ * has throttled requests in the queue.
+ */
+typedef struct ThrottleGroup {
+    char *name; /* This is constant during the lifetime of the group */
+
+    QemuMutex lock; /* This lock protects the following four fields */
+    ThrottleState ts;
+    QLIST_HEAD(, BlockDriverState) head;
+    BlockDriverState *tokens[2];
+    bool any_timer_armed[2];
+
+    /* These two are protected by the global throttle_groups_lock */
+    unsigned refcount;
+    QTAILQ_ENTRY(ThrottleGroup) list;
+} ThrottleGroup;
+
+static QemuMutex throttle_groups_lock;
+static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
+    QTAILQ_HEAD_INITIALIZER(throttle_groups);
+
+/* Increments the reference count of a ThrottleGroup given its name.
+ *
+ * If no ThrottleGroup is found with the given name a new one is
+ * created.
+ *
+ * @name: the name of the ThrottleGroup
+ * @ret:  the ThrottleGroup
+ */
+static ThrottleGroup *throttle_group_incref(const char *name)
+{
+    ThrottleGroup *tg = NULL;
+    ThrottleGroup *iter;
+
+    qemu_mutex_lock(&throttle_groups_lock);
+
+    /* Look for an existing group with that name */
+    QTAILQ_FOREACH(iter, &throttle_groups, list) {
+        if (!strcmp(name, iter->name)) {
+            tg = iter;
+            break;
+        }
+    }
+
+    /* Create a new one if not found */
+    if (!tg) {
+        tg = g_new0(ThrottleGroup, 1);
+        tg->name = g_strdup(name);
+        qemu_mutex_init(&tg->lock);
+        throttle_init(&tg->ts);
+        QLIST_INIT(&tg->head);
+
+        QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
+    }
+
+    tg->refcount++;
+
+    qemu_mutex_unlock(&throttle_groups_lock);
+
+    return tg;
+}
+
+/* Decrease the reference count of a ThrottleGroup.
+ *
+ * When the reference count reaches zero the ThrottleGroup is
+ * destroyed.
+ *
+ * @tg:  The ThrottleGroup to unref
+ */
+static void throttle_group_unref(ThrottleGroup *tg)
+{
+    qemu_mutex_lock(&throttle_groups_lock);
+    if (--tg->refcount == 0) {
+        QTAILQ_REMOVE(&throttle_groups, tg, list);
+        qemu_mutex_destroy(&tg->lock);
+        g_free(tg->name);
+        g_free(tg);
+    }
+    qemu_mutex_unlock(&throttle_groups_lock);
+}
+
+/* Get the name from a BlockDriverState's ThrottleGroup. The name (and
+ * the pointer) is guaranteed to remain constant during the lifetime
+ * of the group.
+ *
+ * @bs:   a BlockDriverState that is member of a throttling group
+ * @ret:  the name of the group.
+ */
+const char *throttle_group_get_name(BlockDriverState *bs)
+{
+    ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+    return tg->name;
+}
+
+/* Return the next BlockDriverState in the round-robin sequence,
+ * simulating a circular list.
+ *
+ * This assumes that tg->lock is held.
+ *
+ * @bs:  the current BlockDriverState
+ * @ret: the next BlockDriverState in the sequence
+ */
+static BlockDriverState *throttle_group_next_bs(BlockDriverState *bs)
+{
+    ThrottleState *ts = bs->throttle_state;
+    ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+    BlockDriverState *next = QLIST_NEXT(bs, round_robin);
+
+    if (!next) {
+        return QLIST_FIRST(&tg->head);
+    }
+
+    return next;
+}
+
+/* Return the next BlockDriverState in the round-robin sequence with
+ * pending I/O requests.
+ *
+ * This assumes that tg->lock is held.
+ *
+ * @bs:        the current BlockDriverState
+ * @is_write:  the type of operation (read/write)
+ * @ret:       the next BlockDriverState with pending requests, or bs
+ *             if there is none.
+ */
+static BlockDriverState *next_throttle_token(BlockDriverState *bs,
+                                             bool is_write)
+{
+    ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+    BlockDriverState *token, *start;
+
+    start = token = tg->tokens[is_write];
+
+    /* get next bs round in round robin style */
+    token = throttle_group_next_bs(token);
+    while (token != start && !token->pending_reqs[is_write]) {
+        token = throttle_group_next_bs(token);
+    }
+
+    /* If no IO are queued for scheduling on the next round robin token
+     * then decide the token is the current bs because chances are
+     * the current bs get the current request queued.
+     */
+    if (token == start && !token->pending_reqs[is_write]) {
+        token = bs;
+    }
+
+    return token;
+}
+
+/* Check if the next I/O request for a BlockDriverState needs to be
+ * throttled or not. If there's no timer set in this group, set one
+ * and update the token accordingly.
+ *
+ * This assumes that tg->lock is held.
+ *
+ * @bs:         the current BlockDriverState
+ * @is_write:   the type of operation (read/write)
+ * @ret:        whether the I/O request needs to be throttled or not
+ */
+static bool throttle_group_schedule_timer(BlockDriverState *bs,
+                                          bool is_write)
+{
+    ThrottleState *ts = bs->throttle_state;
+    ThrottleTimers *tt = &bs->throttle_timers;
+    ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+    bool must_wait;
+
+    /* Check if any of the timers in this group is already armed */
+    if (tg->any_timer_armed[is_write]) {
+        return true;
+    }
+
+    must_wait = throttle_schedule_timer(ts, tt, is_write);
+
+    /* If a timer just got armed, set bs as the current token */
+    if (must_wait) {
+        tg->tokens[is_write] = bs;
+        tg->any_timer_armed[is_write] = true;
+    }
+
+    return must_wait;
+}
+
+/* Look for the next pending I/O request and schedule it.
+ *
+ * This assumes that tg->lock is held.
+ *
+ * @bs:        the current BlockDriverState
+ * @is_write:  the type of operation (read/write)
+ */
+static void schedule_next_request(BlockDriverState *bs, bool is_write)
+{
+    ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+    bool must_wait;
+    BlockDriverState *token;
+
+    /* Check if there's any pending request to schedule next */
+    token = next_throttle_token(bs, is_write);
+    if (!token->pending_reqs[is_write]) {
+        return;
+    }
+
+    /* Set a timer for the request if it needs to be throttled */
+    must_wait = throttle_group_schedule_timer(token, is_write);
+
+    /* If it doesn't have to wait, queue it for immediate execution */
+    if (!must_wait) {
+        /* Give preference to requests from the current bs */
+        if (qemu_in_coroutine() &&
+            qemu_co_queue_next(&bs->throttled_reqs[is_write])) {
+            token = bs;
+        } else {
+            ThrottleTimers *tt = &token->throttle_timers;
+            int64_t now = qemu_clock_get_ns(tt->clock_type);
+            timer_mod(tt->timers[is_write], now + 1);
+            tg->any_timer_armed[is_write] = true;
+        }
+        tg->tokens[is_write] = token;
+    }
+}
+
+/* Check if an I/O request needs to be throttled, wait and set a timer
+ * if necessary, and schedule the next request using a round robin
+ * algorithm.
+ *
+ * @bs:        the current BlockDriverState
+ * @bytes:     the number of bytes for this I/O
+ * @is_write:  the type of operation (read/write)
+ */
+void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs,
+                                                        unsigned int bytes,
+                                                        bool is_write)
+{
+    bool must_wait;
+    BlockDriverState *token;
+
+    ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+    qemu_mutex_lock(&tg->lock);
+
+    /* First we check if this I/O has to be throttled. */
+    token = next_throttle_token(bs, is_write);
+    must_wait = throttle_group_schedule_timer(token, is_write);
+
+    /* Wait if there's a timer set or queued requests of this type */
+    if (must_wait || bs->pending_reqs[is_write]) {
+        bs->pending_reqs[is_write]++;
+        qemu_mutex_unlock(&tg->lock);
+        qemu_co_queue_wait(&bs->throttled_reqs[is_write]);
+        qemu_mutex_lock(&tg->lock);
+        bs->pending_reqs[is_write]--;
+    }
+
+    /* The I/O will be executed, so do the accounting */
+    throttle_account(bs->throttle_state, is_write, bytes);
+
+    /* Schedule the next request */
+    schedule_next_request(bs, is_write);
+
+    qemu_mutex_unlock(&tg->lock);
+}
+
+/* Update the throttle configuration for a particular group. Similar
+ * to throttle_config(), but guarantees atomicity within the
+ * throttling group.
+ *
+ * @bs:  a BlockDriverState that is member of the group
+ * @cfg: the configuration to set
+ */
+void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg)
+{
+    ThrottleTimers *tt = &bs->throttle_timers;
+    ThrottleState *ts = bs->throttle_state;
+    ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+    qemu_mutex_lock(&tg->lock);
+    throttle_config(ts, tt, cfg);
+    /* throttle_config() cancels the timers */
+    tg->any_timer_armed[0] = tg->any_timer_armed[1] = false;
+    qemu_mutex_unlock(&tg->lock);
+}
+
+/* Get the throttle configuration from a particular group. Similar to
+ * throttle_get_config(), but guarantees atomicity within the
+ * throttling group.
+ *
+ * @bs:  a BlockDriverState that is member of the group
+ * @cfg: the configuration will be written here
+ */
+void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg)
+{
+    ThrottleState *ts = bs->throttle_state;
+    ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+    qemu_mutex_lock(&tg->lock);
+    throttle_get_config(ts, cfg);
+    qemu_mutex_unlock(&tg->lock);
+}
+
+/* ThrottleTimers callback. This wakes up a request that was waiting
+ * because it had been throttled.
+ *
+ * @bs:        the BlockDriverState whose request had been throttled
+ * @is_write:  the type of operation (read/write)
+ */
+static void timer_cb(BlockDriverState *bs, bool is_write)
+{
+    ThrottleState *ts = bs->throttle_state;
+    ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+    bool empty_queue;
+
+    /* The timer has just been fired, so we can update the flag */
+    qemu_mutex_lock(&tg->lock);
+    tg->any_timer_armed[is_write] = false;
+    qemu_mutex_unlock(&tg->lock);
+
+    /* Run the request that was waiting for this timer */
+    empty_queue = !qemu_co_enter_next(&bs->throttled_reqs[is_write]);
+
+    /* If the request queue was empty then we have to take care of
+     * scheduling the next one */
+    if (empty_queue) {
+        qemu_mutex_lock(&tg->lock);
+        schedule_next_request(bs, is_write);
+        qemu_mutex_unlock(&tg->lock);
+    }
+}
+
+static void read_timer_cb(void *opaque)
+{
+    timer_cb(opaque, false);
+}
+
+static void write_timer_cb(void *opaque)
+{
+    timer_cb(opaque, true);
+}
+
+/* Register a BlockDriverState in the throttling group, also
+ * initializing its timers and updating its throttle_state pointer to
+ * point to it. If a throttling group with that name does not exist
+ * yet, it will be created.
+ *
+ * @bs:        the BlockDriverState to insert
+ * @groupname: the name of the group
+ */
+void throttle_group_register_bs(BlockDriverState *bs, const char *groupname)
+{
+    int i;
+    ThrottleGroup *tg = throttle_group_incref(groupname);
+    int clock_type = QEMU_CLOCK_REALTIME;
+
+    if (qtest_enabled()) {
+        /* For testing block IO throttling only */
+        clock_type = QEMU_CLOCK_VIRTUAL;
+    }
+
+    bs->throttle_state = &tg->ts;
+
+    qemu_mutex_lock(&tg->lock);
+    /* If the ThrottleGroup is new set this BlockDriverState as the token */
+    for (i = 0; i < 2; i++) {
+        if (!tg->tokens[i]) {
+            tg->tokens[i] = bs;
+        }
+    }
+
+    QLIST_INSERT_HEAD(&tg->head, bs, round_robin);
+
+    throttle_timers_init(&bs->throttle_timers,
+                         bdrv_get_aio_context(bs),
+                         clock_type,
+                         read_timer_cb,
+                         write_timer_cb,
+                         bs);
+
+    qemu_mutex_unlock(&tg->lock);
+}
+
+/* Unregister a BlockDriverState from its group, removing it from the
+ * list, destroying the timers and setting the throttle_state pointer
+ * to NULL.
+ *
+ * The group will be destroyed if it's empty after this operation.
+ *
+ * @bs: the BlockDriverState to remove
+ */
+void throttle_group_unregister_bs(BlockDriverState *bs)
+{
+    ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+    int i;
+
+    qemu_mutex_lock(&tg->lock);
+    for (i = 0; i < 2; i++) {
+        if (tg->tokens[i] == bs) {
+            BlockDriverState *token = throttle_group_next_bs(bs);
+            /* Take care of the case where this is the last bs in the group */
+            if (token == bs) {
+                token = NULL;
+            }
+            tg->tokens[i] = token;
+        }
+    }
+
+    /* remove the current bs from the list */
+    QLIST_REMOVE(bs, round_robin);
+    throttle_timers_destroy(&bs->throttle_timers);
+    qemu_mutex_unlock(&tg->lock);
+
+    throttle_group_unref(tg);
+    bs->throttle_state = NULL;
+}
+
+/* Acquire the lock of this throttling group.
+ *
+ * You won't normally need to use this. None of the functions from the
+ * ThrottleGroup API require you to acquire the lock since all of them
+ * deal with it internally.
+ *
+ * This should only be used in exceptional cases when you want to
+ * access the protected fields of a BlockDriverState directly
+ * (e.g. bdrv_swap()).
+ *
+ * @bs: a BlockDriverState that is member of the group
+ */
+void throttle_group_lock(BlockDriverState *bs)
+{
+    ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+    qemu_mutex_lock(&tg->lock);
+}
+
+/* Release the lock of this throttling group.
+ *
+ * See the comments in throttle_group_lock().
+ */
+void throttle_group_unlock(BlockDriverState *bs)
+{
+    ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+    qemu_mutex_unlock(&tg->lock);
+}
+
+static void throttle_groups_init(void)
+{
+    qemu_mutex_init(&throttle_groups_lock);
+}
+
+block_init(throttle_groups_init);
diff --git a/block/vmdk.c b/block/vmdk.c
index b66745dfdd..be9263a34e 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -321,37 +321,13 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
     return 1;
 }
 
-/* Queue extents, if any, for reopen() */
+/* We have nothing to do for VMDK reopen, stubs just return success */
 static int vmdk_reopen_prepare(BDRVReopenState *state,
                                BlockReopenQueue *queue, Error **errp)
 {
-    BDRVVmdkState *s;
-    int ret = -1;
-    int i;
-    VmdkExtent *e;
-
     assert(state != NULL);
     assert(state->bs != NULL);
-
-    if (queue == NULL) {
-        error_setg(errp, "No reopen queue for VMDK extents");
-        goto exit;
-    }
-
-    s = state->bs->opaque;
-
-    assert(s != NULL);
-
-    for (i = 0; i < s->num_extents; i++) {
-        e = &s->extents[i];
-        if (e->file != state->bs->file) {
-            bdrv_reopen_queue(queue, e->file, state->flags);
-        }
-    }
-    ret = 0;
-
-exit:
-    return ret;
+    return 0;
 }
 
 static int vmdk_parent_open(BlockDriverState *bs)
@@ -543,7 +519,7 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
 }
 
 static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
-                               Error **errp);
+                               QDict *options, Error **errp);
 
 static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
                             Error **errp)
@@ -582,7 +558,7 @@ static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
 
 static int vmdk_open_vmdk4(BlockDriverState *bs,
                            BlockDriverState *file,
-                           int flags, Error **errp)
+                           int flags, QDict *options, Error **errp)
 {
     int ret;
     uint32_t magic;
@@ -606,7 +582,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
             if (!buf) {
                 return -EINVAL;
             }
-            ret = vmdk_open_desc_file(bs, flags, buf, errp);
+            ret = vmdk_open_desc_file(bs, flags, buf, options, errp);
             g_free(buf);
             return ret;
         }
@@ -763,7 +739,7 @@ static int vmdk_parse_description(const char *desc, const char *opt_name,
 /* Open an extent file and append to bs array */
 static int vmdk_open_sparse(BlockDriverState *bs,
                             BlockDriverState *file, int flags,
-                            char *buf, Error **errp)
+                            char *buf, QDict *options, Error **errp)
 {
     uint32_t magic;
 
@@ -773,7 +749,7 @@ static int vmdk_open_sparse(BlockDriverState *bs,
             return vmdk_open_vmfs_sparse(bs, file, flags, errp);
             break;
         case VMDK4_MAGIC:
-            return vmdk_open_vmdk4(bs, file, flags, errp);
+            return vmdk_open_vmdk4(bs, file, flags, options, errp);
             break;
         default:
             error_setg(errp, "Image not in VMDK format");
@@ -783,7 +759,8 @@ static int vmdk_open_sparse(BlockDriverState *bs,
 }
 
 static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
-                              const char *desc_file_path, Error **errp)
+                              const char *desc_file_path, QDict *options,
+                              Error **errp)
 {
     int ret;
     int matches;
@@ -797,6 +774,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
     BlockDriverState *extent_file;
     BDRVVmdkState *s = bs->opaque;
     VmdkExtent *extent;
+    char extent_opt_prefix[32];
 
     while (*p) {
         /* parse extent line in one of below formats:
@@ -846,8 +824,12 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
         extent_path = g_malloc0(PATH_MAX);
         path_combine(extent_path, PATH_MAX, desc_file_path, fname);
         extent_file = NULL;
-        ret = bdrv_open(&extent_file, extent_path, NULL, NULL,
-                        bs->open_flags | BDRV_O_PROTOCOL, NULL, errp);
+
+        ret = snprintf(extent_opt_prefix, 32, "extents.%d", s->num_extents);
+        assert(ret < 32);
+
+        ret = bdrv_open_image(&extent_file, extent_path, options,
+                              extent_opt_prefix, bs, &child_file, false, errp);
         g_free(extent_path);
         if (ret) {
             return ret;
@@ -870,7 +852,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
             if (!buf) {
                 ret = -EINVAL;
             } else {
-                ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp);
+                ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf,
+                                       options, errp);
             }
             g_free(buf);
             if (ret) {
@@ -898,7 +881,7 @@ next_line:
 }
 
 static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
-                               Error **errp)
+                               QDict *options, Error **errp)
 {
     int ret;
     char ct[128];
@@ -920,7 +903,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
     }
     s->create_type = g_strdup(ct);
     s->desc_offset = 0;
-    ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, errp);
+    ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, options, errp);
 exit:
     return ret;
 }
@@ -942,11 +925,11 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
     switch (magic) {
         case VMDK3_MAGIC:
         case VMDK4_MAGIC:
-            ret = vmdk_open_sparse(bs, bs->file, flags, buf, errp);
+            ret = vmdk_open_sparse(bs, bs->file, flags, buf, options, errp);
             s->desc_offset = 0x200;
             break;
         default:
-            ret = vmdk_open_desc_file(bs, flags, buf, errp);
+            ret = vmdk_open_desc_file(bs, flags, buf, options, errp);
             break;
     }
     if (ret) {
@@ -1248,6 +1231,17 @@ static VmdkExtent *find_extent(BDRVVmdkState *s,
     return NULL;
 }
 
+static inline uint64_t vmdk_find_index_in_cluster(VmdkExtent *extent,
+                                                  int64_t sector_num)
+{
+    uint64_t index_in_cluster, extent_begin_sector, extent_relative_sector_num;
+
+    extent_begin_sector = extent->end_sector - extent->sectors;
+    extent_relative_sector_num = sector_num - extent_begin_sector;
+    index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
+    return index_in_cluster;
+}
+
 static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
         int64_t sector_num, int nb_sectors, int *pnum)
 {
@@ -1285,7 +1279,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
         break;
     }
 
-    index_in_cluster = sector_num % extent->cluster_sectors;
+    index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
     n = extent->cluster_sectors - index_in_cluster;
     if (n > nb_sectors) {
         n = nb_sectors;
@@ -1413,7 +1407,6 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
     BDRVVmdkState *s = bs->opaque;
     int ret;
     uint64_t n, index_in_cluster;
-    uint64_t extent_begin_sector, extent_relative_sector_num;
     VmdkExtent *extent = NULL;
     uint64_t cluster_offset;
 
@@ -1425,9 +1418,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
         ret = get_cluster_offset(bs, extent, NULL,
                                  sector_num << 9, false, &cluster_offset,
                                  0, 0);
-        extent_begin_sector = extent->end_sector - extent->sectors;
-        extent_relative_sector_num = sector_num - extent_begin_sector;
-        index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
+        index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
         n = extent->cluster_sectors - index_in_cluster;
         if (n > nb_sectors) {
             n = nb_sectors;
@@ -1489,7 +1480,6 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
     VmdkExtent *extent = NULL;
     int ret;
     int64_t index_in_cluster, n;
-    uint64_t extent_begin_sector, extent_relative_sector_num;
     uint64_t cluster_offset;
     VmdkMetaData m_data;
 
@@ -1505,9 +1495,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
         if (!extent) {
             return -EIO;
         }
-        extent_begin_sector = extent->end_sector - extent->sectors;
-        extent_relative_sector_num = sector_num - extent_begin_sector;
-        index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
+        index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
         n = extent->cluster_sectors - index_in_cluster;
         if (n > nb_sectors) {
             n = nb_sectors;
diff --git a/blockdev.c b/blockdev.c
index de94a8bcb3..3aa1ae6698 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -34,6 +34,7 @@
 #include "sysemu/blockdev.h"
 #include "hw/block/block.h"
 #include "block/blockjob.h"
+#include "block/throttle-groups.h"
 #include "monitor/monitor.h"
 #include "qemu/option.h"
 #include "qemu/config-file.h"
@@ -357,6 +358,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
     const char *id;
     bool has_driver_specific_opts;
     BlockdevDetectZeroesOptions detect_zeroes;
+    const char *throttling_group;
 
     /* Check common options by copying from bs_opts to opts, all other options
      * stay in bs_opts for processing by bdrv_open(). */
@@ -391,13 +393,13 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
         }
     }
 
-    if (qemu_opt_get_bool(opts, "cache.writeback", true)) {
+    if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) {
         bdrv_flags |= BDRV_O_CACHE_WB;
     }
-    if (qemu_opt_get_bool(opts, "cache.direct", false)) {
+    if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
         bdrv_flags |= BDRV_O_NOCACHE;
     }
-    if (qemu_opt_get_bool(opts, "cache.no-flush", false)) {
+    if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
         bdrv_flags |= BDRV_O_NO_FLUSH;
     }
 
@@ -459,6 +461,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
 
     cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0);
 
+    throttling_group = qemu_opt_get(opts, "throttling.group");
+
     if (!check_throttle_config(&cfg, &error)) {
         error_propagate(errp, error);
         goto early_err;
@@ -547,7 +551,10 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
 
     /* disk I/O throttling */
     if (throttle_enabled(&cfg)) {
-        bdrv_io_limits_enable(bs);
+        if (!throttling_group) {
+            throttling_group = blk_name(blk);
+        }
+        bdrv_io_limits_enable(bs, throttling_group);
         bdrv_set_io_limits(bs, &cfg);
     }
 
@@ -711,6 +718,8 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
 
         { "iops_size",      "throttling.iops-size" },
 
+        { "group",          "throttling.group" },
+
         { "readonly",       "read-only" },
     };
 
@@ -733,16 +742,16 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
         }
 
         /* Specific options take precedence */
-        if (!qemu_opt_get(all_opts, "cache.writeback")) {
-            qemu_opt_set_bool(all_opts, "cache.writeback",
+        if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_WB)) {
+            qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_WB,
                               !!(flags & BDRV_O_CACHE_WB), &error_abort);
         }
-        if (!qemu_opt_get(all_opts, "cache.direct")) {
-            qemu_opt_set_bool(all_opts, "cache.direct",
+        if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_DIRECT)) {
+            qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_DIRECT,
                               !!(flags & BDRV_O_NOCACHE), &error_abort);
         }
-        if (!qemu_opt_get(all_opts, "cache.no-flush")) {
-            qemu_opt_set_bool(all_opts, "cache.no-flush",
+        if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_NO_FLUSH)) {
+            qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_NO_FLUSH,
                               !!(flags & BDRV_O_NO_FLUSH), &error_abort);
         }
         qemu_opt_unset(all_opts, "cache");
@@ -1951,7 +1960,9 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
                                bool has_iops_wr_max,
                                int64_t iops_wr_max,
                                bool has_iops_size,
-                               int64_t iops_size, Error **errp)
+                               int64_t iops_size,
+                               bool has_group,
+                               const char *group, Error **errp)
 {
     ThrottleConfig cfg;
     BlockDriverState *bs;
@@ -2004,14 +2015,19 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
     aio_context = bdrv_get_aio_context(bs);
     aio_context_acquire(aio_context);
 
-    if (!bs->io_limits_enabled && throttle_enabled(&cfg)) {
-        bdrv_io_limits_enable(bs);
-    } else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) {
-        bdrv_io_limits_disable(bs);
-    }
-
-    if (bs->io_limits_enabled) {
+    if (throttle_enabled(&cfg)) {
+        /* Enable I/O limits if they're not enabled yet, otherwise
+         * just update the throttling group. */
+        if (!bs->io_limits_enabled) {
+            bdrv_io_limits_enable(bs, has_group ? group : device);
+        } else if (has_group) {
+            bdrv_io_limits_update_group(bs, group);
+        }
+        /* Set the new throttling configuration */
         bdrv_set_io_limits(bs, &cfg);
+    } else if (bs->io_limits_enabled) {
+        /* If all throttling settings are set to 0, disable I/O limits */
+        bdrv_io_limits_disable(bs);
     }
 
     aio_context_release(aio_context);
@@ -3105,15 +3121,15 @@ QemuOptsList qemu_common_drive_opts = {
             .type = QEMU_OPT_STRING,
             .help = "discard operation (ignore/off, unmap/on)",
         },{
-            .name = "cache.writeback",
+            .name = BDRV_OPT_CACHE_WB,
             .type = QEMU_OPT_BOOL,
             .help = "enables writeback mode for any caches",
         },{
-            .name = "cache.direct",
+            .name = BDRV_OPT_CACHE_DIRECT,
             .type = QEMU_OPT_BOOL,
             .help = "enables use of O_DIRECT (bypass the host page cache)",
         },{
-            .name = "cache.no-flush",
+            .name = BDRV_OPT_CACHE_NO_FLUSH,
             .type = QEMU_OPT_BOOL,
             .help = "ignore any flush requests for the device",
         },{
@@ -3189,6 +3205,10 @@ QemuOptsList qemu_common_drive_opts = {
             .type = QEMU_OPT_NUMBER,
             .help = "when limiting by iops max size of an I/O in bytes",
         },{
+            .name = "throttling.group",
+            .type = QEMU_OPT_STRING,
+            .help = "name of the block throttling group",
+        },{
             .name = "copy-on-read",
             .type = QEMU_OPT_BOOL,
             .help = "copy read data from backing file into image file",
diff --git a/configure b/configure
index 409edf94fb..222694f34d 100755
--- a/configure
+++ b/configure
@@ -285,8 +285,6 @@ sysconfdir="\${prefix}/etc"
 local_statedir="\${prefix}/var"
 confsuffix="/qemu"
 slirp="yes"
-fmod_lib=""
-fmod_inc=""
 oss_lib=""
 bsd="no"
 linux="no"
@@ -437,6 +435,14 @@ EOF
   compile_object
 }
 
+check_include() {
+cat > $TMPC <<EOF
+#include <$1>
+int main(void) { return 0; }
+EOF
+  compile_object
+}
+
 write_c_skeleton() {
     cat > $TMPC <<EOF
 int main(void) { return 0; }
@@ -564,24 +570,28 @@ case $targetos in
 CYGWIN*)
   mingw32="yes"
   QEMU_CFLAGS="-mno-cygwin $QEMU_CFLAGS"
-  audio_possible_drivers="winwave sdl"
-  audio_drv_list="winwave"
+  audio_possible_drivers="sdl"
+  audio_drv_list="sdl"
 ;;
 MINGW32*)
   mingw32="yes"
-  audio_possible_drivers="winwave dsound sdl fmod"
-  audio_drv_list="winwave"
+  audio_possible_drivers="dsound sdl"
+  if check_include dsound.h; then
+    audio_drv_list="dsound"
+  else
+    audio_drv_list=""
+  fi
 ;;
 GNU/kFreeBSD)
   bsd="yes"
   audio_drv_list="oss"
-  audio_possible_drivers="oss sdl esd pa"
+  audio_possible_drivers="oss sdl pa"
 ;;
 FreeBSD)
   bsd="yes"
   make="${MAKE-gmake}"
   audio_drv_list="oss"
-  audio_possible_drivers="oss sdl esd pa"
+  audio_possible_drivers="oss sdl pa"
   # needed for kinfo_getvmmap(3) in libutil.h
   LIBS="-lutil $LIBS"
   netmap=""  # enable netmap autodetect
@@ -591,14 +601,14 @@ DragonFly)
   bsd="yes"
   make="${MAKE-gmake}"
   audio_drv_list="oss"
-  audio_possible_drivers="oss sdl esd pa"
+  audio_possible_drivers="oss sdl pa"
   HOST_VARIANT_DIR="dragonfly"
 ;;
 NetBSD)
   bsd="yes"
   make="${MAKE-gmake}"
   audio_drv_list="oss"
-  audio_possible_drivers="oss sdl esd"
+  audio_possible_drivers="oss sdl"
   oss_lib="-lossaudio"
   HOST_VARIANT_DIR="netbsd"
 ;;
@@ -606,7 +616,7 @@ OpenBSD)
   bsd="yes"
   make="${MAKE-gmake}"
   audio_drv_list="sdl"
-  audio_possible_drivers="sdl esd"
+  audio_possible_drivers="sdl"
   HOST_VARIANT_DIR="openbsd"
 ;;
 Darwin)
@@ -619,7 +629,7 @@ Darwin)
   fi
   cocoa="yes"
   audio_drv_list="coreaudio"
-  audio_possible_drivers="coreaudio sdl fmod"
+  audio_possible_drivers="coreaudio sdl"
   LDFLAGS="-framework CoreFoundation -framework IOKit $LDFLAGS"
   libs_softmmu="-F/System/Library/Frameworks -framework Cocoa -framework IOKit $libs_softmmu"
   # Disable attempts to use ObjectiveC features in os/object.h since they
@@ -674,15 +684,12 @@ Haiku)
 ;;
 *)
   audio_drv_list="oss"
-  audio_possible_drivers="oss alsa sdl esd pa"
+  audio_possible_drivers="oss alsa sdl pa"
   linux="yes"
   linux_user="yes"
   kvm="yes"
   vhost_net="yes"
   vhost_scsi="yes"
-  if [ "$cpu" = "i386" -o "$cpu" = "x86_64" -o "$cpu" = "x32" ] ; then
-    audio_possible_drivers="$audio_possible_drivers fmod"
-  fi
   QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$(pwd)/linux-headers $QEMU_INCLUDES"
 ;;
 esac
@@ -847,10 +854,6 @@ for opt do
   ;;
   --enable-vnc) vnc="yes"
   ;;
-  --fmod-lib=*) fmod_lib="$optarg"
-  ;;
-  --fmod-inc=*) fmod_inc="$optarg"
-  ;;
   --oss-lib=*) oss_lib="$optarg"
   ;;
   --audio-drv-list=*) audio_drv_list="$optarg"
@@ -1349,8 +1352,6 @@ Advanced options (experts only):
   --disable-guest-base     disable GUEST_BASE support
   --enable-pie             build Position Independent Executables
   --disable-pie            do not build Position Independent Executables
-  --fmod-lib               path to FMOD library
-  --fmod-inc               path to FMOD includes
   --oss-lib                path to OSS library
   --cpu=CPU                Build for host CPU [$cpu]
   --disable-uuid           disable uuid support
@@ -2621,21 +2622,6 @@ for drv in $audio_drv_list; do
     libs_softmmu="-lasound $libs_softmmu"
     ;;
 
-    fmod)
-    if test -z $fmod_lib || test -z $fmod_inc; then
-        error_exit "You must specify path to FMOD library and headers" \
-            "Example: --fmod-inc=/path/include/fmod --fmod-lib=/path/lib/libfmod-3.74.so"
-    fi
-    audio_drv_probe $drv fmod.h $fmod_lib "return FSOUND_GetVersion();" "-I $fmod_inc"
-    libs_softmmu="$fmod_lib $libs_softmmu"
-    ;;
-
-    esd)
-    audio_drv_probe $drv esd.h -lesd 'return esd_play_stream(0, 0, "", 0);'
-    libs_softmmu="-lesd $libs_softmmu"
-    audio_pt_int="yes"
-    ;;
-
     pa)
     audio_drv_probe $drv pulse/mainloop.h "-lpulse" \
         "pa_mainloop *m = 0; pa_mainloop_free (m); return 0;"
@@ -2660,11 +2646,6 @@ for drv in $audio_drv_list; do
     # XXX: Probes for CoreAudio, DirectSound, SDL(?)
     ;;
 
-    winwave)
-      libs_softmmu="-lwinmm $libs_softmmu"
-      audio_win_int="yes"
-    ;;
-
     *)
     echo "$audio_possible_drivers" | grep -q "\<$drv\>" || {
         error_exit "Unknown driver '$drv' selected" \
@@ -4629,9 +4610,6 @@ echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak
 for drv in $audio_drv_list; do
     def=CONFIG_`echo $drv | LC_ALL=C tr '[a-z]' '[A-Z]'`
     echo "$def=y" >> $config_host_mak
-    if test "$drv" = "fmod"; then
-        echo "FMOD_CFLAGS=-I$fmod_inc" >> $config_host_mak
-    fi
 done
 if test "$audio_pt_int" = "yes" ; then
   echo "CONFIG_AUDIO_PT_INT=y" >> $config_host_mak
diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak
index f9e13f177e..36e15de336 100644
--- a/default-configs/s390x-softmmu.mak
+++ b/default-configs/s390x-softmmu.mak
@@ -4,3 +4,4 @@ CONFIG_VIRTIO=y
 CONFIG_SCLPCONSOLE=y
 CONFIG_S390_FLIC=y
 CONFIG_S390_FLIC_KVM=$(CONFIG_KVM)
+CONFIG_WDT_DIAG288=y
diff --git a/hmp.c b/hmp.c
index 1e7cac02ac..23abc7d9f1 100644
--- a/hmp.c
+++ b/hmp.c
@@ -400,7 +400,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info,
                         " iops_max=%" PRId64
                         " iops_rd_max=%" PRId64
                         " iops_wr_max=%" PRId64
-                        " iops_size=%" PRId64 "\n",
+                        " iops_size=%" PRId64
+                        " group=%s\n",
                         inserted->bps,
                         inserted->bps_rd,
                         inserted->bps_wr,
@@ -413,7 +414,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info,
                         inserted->iops_max,
                         inserted->iops_rd_max,
                         inserted->iops_wr_max,
-                        inserted->iops_size);
+                        inserted->iops_size,
+                        inserted->group);
     }
 
     if (verbose) {
@@ -1357,7 +1359,9 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict)
                               false,
                               0,
                               false, /* No default I/O size */
-                              0, &err);
+                              0,
+                              false,
+                              NULL, &err);
     hmp_handle_error(mon, &err);
 }
 
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index d036624948..1e7fd28daa 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -574,15 +574,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
     struct arm_boot_info *info =
         container_of(n, struct arm_boot_info, load_kernel_notifier);
 
-    /* CPU objects (unlike devices) are not automatically reset on system
-     * reset, so we must always register a handler to do so. If we're
-     * actually loading a kernel, the handler is also responsible for
-     * arranging that we start it correctly.
-     */
-    for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) {
-        qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
-    }
-
     /* Load the kernel.  */
     if (!info->kernel_filename || info->firmware_loaded) {
 
@@ -783,7 +774,18 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
 
 void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
 {
+    CPUState *cs;
+
     info->load_kernel_notifier.cpu = cpu;
     info->load_kernel_notifier.notifier.notify = arm_load_kernel_notify;
     qemu_add_machine_init_done_notifier(&info->load_kernel_notifier.notifier);
+
+    /* CPU objects (unlike devices) are not automatically reset on system
+     * reset, so we must always register a handler to do so. If we're
+     * actually loading a kernel, the handler is also responsible for
+     * arranging that we start it correctly.
+     */
+    for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) {
+        qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
+    }
 }
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index f921a5680c..ec353f79c4 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -334,10 +334,10 @@ static uint64_t pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri)
 static const ARMCPRegInfo pxa_cp_reginfo[] = {
     /* cp14 crm==1: perf registers */
     { .name = "CPPMNC", .cp = 14, .crn = 0, .crm = 1, .opc1 = 0, .opc2 = 0,
-      .access = PL1_RW,
+      .access = PL1_RW, .type = ARM_CP_IO,
       .readfn = pxa2xx_cppmnc_read, .writefn = pxa2xx_cppmnc_write },
     { .name = "CPCCNT", .cp = 14, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0,
-      .access = PL1_RW,
+      .access = PL1_RW, .type = ARM_CP_IO,
       .readfn = pxa2xx_cpccnt_read, .writefn = arm_cp_write_ignore },
     { .name = "CPINTEN", .cp = 14, .crn = 4, .crm = 1, .opc1 = 0, .opc2 = 0,
       .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
@@ -356,11 +356,11 @@ static const ARMCPRegInfo pxa_cp_reginfo[] = {
       .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
     /* cp14 crn==6: CLKCFG */
     { .name = "CLKCFG", .cp = 14, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
-      .access = PL1_RW,
+      .access = PL1_RW, .type = ARM_CP_IO,
       .readfn = pxa2xx_clkcfg_read, .writefn = pxa2xx_clkcfg_write },
     /* cp14 crn==7: PWRMODE */
     { .name = "PWRMODE", .cp = 14, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 0,
-      .access = PL1_RW,
+      .access = PL1_RW, .type = ARM_CP_IO,
       .readfn = arm_cp_read_zero, .writefn = pxa2xx_pwrmode_write },
     REGINFO_SENTINEL
 };
@@ -457,7 +457,7 @@ typedef struct {
 
     MemoryRegion iomem;
     qemu_irq irq;
-    int enable;
+    uint32_t enable;
     SSIBus *bus;
 
     uint32_t sscr[2];
@@ -470,10 +470,39 @@ typedef struct {
     uint8_t ssacd;
 
     uint32_t rx_fifo[16];
-    int rx_level;
-    int rx_start;
+    uint32_t rx_level;
+    uint32_t rx_start;
 } PXA2xxSSPState;
 
+static bool pxa2xx_ssp_vmstate_validate(void *opaque, int version_id)
+{
+    PXA2xxSSPState *s = opaque;
+
+    return s->rx_start < sizeof(s->rx_fifo);
+}
+
+static const VMStateDescription vmstate_pxa2xx_ssp = {
+    .name = "pxa2xx-ssp",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(enable, PXA2xxSSPState),
+        VMSTATE_UINT32_ARRAY(sscr, PXA2xxSSPState, 2),
+        VMSTATE_UINT32(sspsp, PXA2xxSSPState),
+        VMSTATE_UINT32(ssto, PXA2xxSSPState),
+        VMSTATE_UINT32(ssitr, PXA2xxSSPState),
+        VMSTATE_UINT32(sssr, PXA2xxSSPState),
+        VMSTATE_UINT8(sstsa, PXA2xxSSPState),
+        VMSTATE_UINT8(ssrsa, PXA2xxSSPState),
+        VMSTATE_UINT8(ssacd, PXA2xxSSPState),
+        VMSTATE_UINT32(rx_level, PXA2xxSSPState),
+        VMSTATE_UINT32(rx_start, PXA2xxSSPState),
+        VMSTATE_VALIDATE("fifo is 16 bytes", pxa2xx_ssp_vmstate_validate),
+        VMSTATE_UINT32_ARRAY(rx_fifo, PXA2xxSSPState, 16),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 #define SSCR0	0x00	/* SSP Control register 0 */
 #define SSCR1	0x04	/* SSP Control register 1 */
 #define SSSR	0x08	/* SSP Status register */
@@ -705,55 +734,20 @@ static const MemoryRegionOps pxa2xx_ssp_ops = {
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
-static void pxa2xx_ssp_save(QEMUFile *f, void *opaque)
+static void pxa2xx_ssp_reset(DeviceState *d)
 {
-    PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
-    int i;
-
-    qemu_put_be32(f, s->enable);
+    PXA2xxSSPState *s = PXA2XX_SSP(d);
 
-    qemu_put_be32s(f, &s->sscr[0]);
-    qemu_put_be32s(f, &s->sscr[1]);
-    qemu_put_be32s(f, &s->sspsp);
-    qemu_put_be32s(f, &s->ssto);
-    qemu_put_be32s(f, &s->ssitr);
-    qemu_put_be32s(f, &s->sssr);
-    qemu_put_8s(f, &s->sstsa);
-    qemu_put_8s(f, &s->ssrsa);
-    qemu_put_8s(f, &s->ssacd);
-
-    qemu_put_byte(f, s->rx_level);
-    for (i = 0; i < s->rx_level; i ++)
-        qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 0xf]);
-}
-
-static int pxa2xx_ssp_load(QEMUFile *f, void *opaque, int version_id)
-{
-    PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
-    int i, v;
-
-    s->enable = qemu_get_be32(f);
-
-    qemu_get_be32s(f, &s->sscr[0]);
-    qemu_get_be32s(f, &s->sscr[1]);
-    qemu_get_be32s(f, &s->sspsp);
-    qemu_get_be32s(f, &s->ssto);
-    qemu_get_be32s(f, &s->ssitr);
-    qemu_get_be32s(f, &s->sssr);
-    qemu_get_8s(f, &s->sstsa);
-    qemu_get_8s(f, &s->ssrsa);
-    qemu_get_8s(f, &s->ssacd);
-
-    v = qemu_get_byte(f);
-    if (v < 0 || v > ARRAY_SIZE(s->rx_fifo)) {
-        return -EINVAL;
-    }
-    s->rx_level = v;
-    s->rx_start = 0;
-    for (i = 0; i < s->rx_level; i ++)
-        s->rx_fifo[i] = qemu_get_byte(f);
-
-    return 0;
+    s->enable = 0;
+    s->sscr[0] = s->sscr[1] = 0;
+    s->sspsp = 0;
+    s->ssto = 0;
+    s->ssitr = 0;
+    s->sssr = 0;
+    s->sstsa = 0;
+    s->ssrsa = 0;
+    s->ssacd = 0;
+    s->rx_start = s->rx_level = 0;
 }
 
 static int pxa2xx_ssp_init(SysBusDevice *sbd)
@@ -766,8 +760,6 @@ static int pxa2xx_ssp_init(SysBusDevice *sbd)
     memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_ssp_ops, s,
                           "pxa2xx-ssp", 0x1000);
     sysbus_init_mmio(sbd, &s->iomem);
-    register_savevm(dev, "pxa2xx_ssp", -1, 0,
-                    pxa2xx_ssp_save, pxa2xx_ssp_load, s);
 
     s->bus = ssi_create_bus(dev, "ssi");
     return 0;
@@ -1759,24 +1751,33 @@ static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem,
 }
 
 /* PXA Fast Infra-red Communications Port */
+#define TYPE_PXA2XX_FIR "pxa2xx-fir"
+#define PXA2XX_FIR(obj) OBJECT_CHECK(PXA2xxFIrState, (obj), TYPE_PXA2XX_FIR)
+
 struct PXA2xxFIrState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
     MemoryRegion iomem;
     qemu_irq irq;
     qemu_irq rx_dma;
     qemu_irq tx_dma;
-    int enable;
+    uint32_t enable;
     CharDriverState *chr;
 
     uint8_t control[3];
     uint8_t status[2];
 
-    int rx_len;
-    int rx_start;
+    uint32_t rx_len;
+    uint32_t rx_start;
     uint8_t rx_fifo[64];
 };
 
-static void pxa2xx_fir_reset(PXA2xxFIrState *s)
+static void pxa2xx_fir_reset(DeviceState *d)
 {
+    PXA2xxFIrState *s = PXA2XX_FIR(d);
+
     s->control[0] = 0x00;
     s->control[1] = 0x00;
     s->control[2] = 0x00;
@@ -1953,73 +1954,94 @@ static void pxa2xx_fir_event(void *opaque, int event)
 {
 }
 
-static void pxa2xx_fir_save(QEMUFile *f, void *opaque)
+static void pxa2xx_fir_instance_init(Object *obj)
 {
-    PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
-    int i;
+    PXA2xxFIrState *s = PXA2XX_FIR(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 
-    qemu_put_be32(f, s->enable);
-
-    qemu_put_8s(f, &s->control[0]);
-    qemu_put_8s(f, &s->control[1]);
-    qemu_put_8s(f, &s->control[2]);
-    qemu_put_8s(f, &s->status[0]);
-    qemu_put_8s(f, &s->status[1]);
-
-    qemu_put_byte(f, s->rx_len);
-    for (i = 0; i < s->rx_len; i ++)
-        qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 63]);
+    memory_region_init_io(&s->iomem, NULL, &pxa2xx_fir_ops, s,
+                          "pxa2xx-fir", 0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq);
+    sysbus_init_irq(sbd, &s->rx_dma);
+    sysbus_init_irq(sbd, &s->tx_dma);
 }
 
-static int pxa2xx_fir_load(QEMUFile *f, void *opaque, int version_id)
+static void pxa2xx_fir_realize(DeviceState *dev, Error **errp)
 {
-    PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
-    int i;
-
-    s->enable = qemu_get_be32(f);
+    PXA2xxFIrState *s = PXA2XX_FIR(dev);
 
-    qemu_get_8s(f, &s->control[0]);
-    qemu_get_8s(f, &s->control[1]);
-    qemu_get_8s(f, &s->control[2]);
-    qemu_get_8s(f, &s->status[0]);
-    qemu_get_8s(f, &s->status[1]);
+    if (s->chr) {
+        qemu_chr_fe_claim_no_fail(s->chr);
+        qemu_chr_add_handlers(s->chr, pxa2xx_fir_is_empty,
+                        pxa2xx_fir_rx, pxa2xx_fir_event, s);
+    }
+}
 
-    s->rx_len = qemu_get_byte(f);
-    s->rx_start = 0;
-    for (i = 0; i < s->rx_len; i ++)
-        s->rx_fifo[i] = qemu_get_byte(f);
+static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id)
+{
+    PXA2xxFIrState *s = opaque;
 
-    return 0;
+    return s->rx_start < ARRAY_SIZE(s->rx_fifo);
 }
 
-static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem,
-                hwaddr base,
-                qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma,
-                CharDriverState *chr)
-{
-    PXA2xxFIrState *s = (PXA2xxFIrState *)
-            g_malloc0(sizeof(PXA2xxFIrState));
+static const VMStateDescription pxa2xx_fir_vmsd = {
+    .name = "pxa2xx-fir",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(enable, PXA2xxFIrState),
+        VMSTATE_UINT8_ARRAY(control, PXA2xxFIrState, 3),
+        VMSTATE_UINT8_ARRAY(status, PXA2xxFIrState, 2),
+        VMSTATE_UINT32(rx_len, PXA2xxFIrState),
+        VMSTATE_UINT32(rx_start, PXA2xxFIrState),
+        VMSTATE_VALIDATE("fifo is 64 bytes", pxa2xx_fir_vmstate_validate),
+        VMSTATE_UINT8_ARRAY(rx_fifo, PXA2xxFIrState, 64),
+        VMSTATE_END_OF_LIST()
+    }
+};
 
-    s->irq = irq;
-    s->rx_dma = rx_dma;
-    s->tx_dma = tx_dma;
-    s->chr = chr;
+static Property pxa2xx_fir_properties[] = {
+    DEFINE_PROP_CHR("chardev", PXA2xxFIrState, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
 
-    pxa2xx_fir_reset(s);
+static void pxa2xx_fir_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
 
-    memory_region_init_io(&s->iomem, NULL, &pxa2xx_fir_ops, s, "pxa2xx-fir", 0x1000);
-    memory_region_add_subregion(sysmem, base, &s->iomem);
+    dc->realize = pxa2xx_fir_realize;
+    dc->vmsd = &pxa2xx_fir_vmsd;
+    dc->props = pxa2xx_fir_properties;
+    dc->reset = pxa2xx_fir_reset;
+}
 
-    if (chr) {
-        qemu_chr_fe_claim_no_fail(chr);
-        qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty,
-                        pxa2xx_fir_rx, pxa2xx_fir_event, s);
-    }
+static const TypeInfo pxa2xx_fir_info = {
+    .name = TYPE_PXA2XX_FIR,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PXA2xxFIrState),
+    .class_init = pxa2xx_fir_class_init,
+    .instance_init = pxa2xx_fir_instance_init,
+};
 
-    register_savevm(NULL, "pxa2xx_fir", 0, 0, pxa2xx_fir_save,
-                    pxa2xx_fir_load, s);
+static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem,
+                                       hwaddr base,
+                                       qemu_irq irq, qemu_irq rx_dma,
+                                       qemu_irq tx_dma,
+                                       CharDriverState *chr)
+{
+    DeviceState *dev;
+    SysBusDevice *sbd;
 
-    return s;
+    dev = qdev_create(NULL, TYPE_PXA2XX_FIR);
+    qdev_prop_set_chr(dev, "chardev", chr);
+    qdev_init_nofail(dev);
+    sbd = SYS_BUS_DEVICE(dev);
+    sysbus_mmio_map(sbd, 0, base);
+    sysbus_connect_irq(sbd, 0, irq);
+    sysbus_connect_irq(sbd, 1, rx_dma);
+    sysbus_connect_irq(sbd, 2, tx_dma);
+    return PXA2XX_FIR(dev);
 }
 
 static void pxa2xx_reset(void *opaque, int line, int level)
@@ -2306,8 +2328,11 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
 static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data)
 {
     SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
 
     sdc->init = pxa2xx_ssp_init;
+    dc->reset = pxa2xx_ssp_reset;
+    dc->vmsd = &vmstate_pxa2xx_ssp;
 }
 
 static const TypeInfo pxa2xx_ssp_info = {
@@ -2323,6 +2348,7 @@ static void pxa2xx_register_types(void)
     type_register_static(&pxa2xx_ssp_info);
     type_register_static(&pxa2xx_i2c_info);
     type_register_static(&pxa2xx_rtc_sysbus_info);
+    type_register_static(&pxa2xx_fir_info);
 }
 
 type_init(pxa2xx_register_types)
diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c
index 9cfc714874..d41ac93416 100644
--- a/hw/arm/pxa2xx_pic.c
+++ b/hw/arm/pxa2xx_pic.c
@@ -232,7 +232,7 @@ static void pxa2xx_pic_cp_write(CPUARMState *env, const ARMCPRegInfo *ri,
 
 #define REGINFO_FOR_PIC_CP(NAME, CRN) \
     { .name = NAME, .cp = 6, .crn = CRN, .crm = 0, .opc1 = 0, .opc2 = 0, \
-      .access = PL1_RW, \
+      .access = PL1_RW, .type = ARM_CP_IO, \
       .readfn = pxa2xx_pic_cp_read, .writefn = pxa2xx_pic_cp_write }
 
 static const ARMCPRegInfo pxa_pic_cp_reginfo[] = {
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index a9373ccaca..d5a8b9c017 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -84,6 +84,12 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
                aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
                              AML_EXCLUSIVE, uart_irq));
     aml_append(dev, aml_name_decl("_CRS", crs));
+
+    /* The _ADR entry is used to link this device to the UART described
+     * in the SPCR table, i.e. SPCR.base_address.address == _ADR.
+     */
+    aml_append(dev, aml_name_decl("_ADR", aml_int(uart_memmap->base)));
+
     aml_append(scope, dev);
 }
 
@@ -334,6 +340,38 @@ build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
 }
 
 static void
+build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
+{
+    AcpiSerialPortConsoleRedirection *spcr;
+    const MemMapEntry *uart_memmap = &guest_info->memmap[VIRT_UART];
+    int irq = guest_info->irqmap[VIRT_UART] + ARM_SPI_BASE;
+
+    spcr = acpi_data_push(table_data, sizeof(*spcr));
+
+    spcr->interface_type = 0x3;    /* ARM PL011 UART */
+
+    spcr->base_address.space_id = AML_SYSTEM_MEMORY;
+    spcr->base_address.bit_width = 8;
+    spcr->base_address.bit_offset = 0;
+    spcr->base_address.access_width = 1;
+    spcr->base_address.address = cpu_to_le64(uart_memmap->base);
+
+    spcr->interrupt_types = (1 << 3); /* Bit[3] ARMH GIC interrupt */
+    spcr->gsi = cpu_to_le32(irq);  /* Global System Interrupt */
+
+    spcr->baud = 3;                /* Baud Rate: 3 = 9600 */
+    spcr->parity = 0;              /* No Parity */
+    spcr->stopbits = 1;            /* 1 Stop bit */
+    spcr->flowctrl = (1 << 1);     /* Bit[1] = RTS/CTS hardware flow control */
+    spcr->term_type = 0;           /* Terminal Type: 0 = VT100 */
+
+    spcr->pci_device_id = 0xffff;  /* PCI Device ID: not a PCI device */
+    spcr->pci_vendor_id = 0xffff;  /* PCI Vendor ID: not a PCI device */
+
+    build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2);
+}
+
+static void
 build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
 {
     AcpiTableMcfg *mcfg;
@@ -514,7 +552,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
     dsdt = tables_blob->len;
     build_dsdt(tables_blob, tables->linker, guest_info);
 
-    /* FADT MADT GTDT pointed to by RSDT */
+    /* FADT MADT GTDT SPCR pointed to by RSDT */
     acpi_add_table(table_offsets, tables_blob);
     build_fadt(tables_blob, tables->linker, dsdt);
 
@@ -527,6 +565,9 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
     acpi_add_table(table_offsets, tables_blob);
     build_mcfg(tables_blob, tables->linker, guest_info);
 
+    acpi_add_table(table_offsets, tables_blob);
+    build_spcr(tables_blob, tables->linker, guest_info);
+
     /* RSDT is pointed to by RSDP */
     rsdt = tables_blob->len;
     build_rsdt(tables_blob, tables->linker, table_offsets);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 1b1cc716ad..f1e85c8e92 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -145,6 +145,11 @@ static VirtBoardInfo machines[] = {
         .irqmap = a15irqmap,
     },
     {
+        .cpu_model = "cortex-a53",
+        .memmap = a15memmap,
+        .irqmap = a15irqmap,
+    },
+    {
         .cpu_model = "cortex-a57",
         .memmap = a15memmap,
         .irqmap = a15irqmap,
@@ -306,7 +311,7 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
                                         "enable-method", "psci");
         }
 
-        qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", cpu);
+        qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", armcpu->mp_affinity);
         g_free(nodename);
     }
 }
diff --git a/hw/core/nmi.c b/hw/core/nmi.c
index 3dff020659..5260d6c1ec 100644
--- a/hw/core/nmi.c
+++ b/hw/core/nmi.c
@@ -21,6 +21,7 @@
 
 #include "hw/nmi.h"
 #include "qapi/qmp/qerror.h"
+#include "monitor/monitor.h"
 
 struct do_nmi_s {
     int cpu_index;
@@ -70,6 +71,25 @@ void nmi_monitor_handle(int cpu_index, Error **errp)
     }
 }
 
+void inject_nmi(void)
+{
+#if defined(TARGET_I386)
+    CPUState *cs;
+
+    CPU_FOREACH(cs) {
+        X86CPU *cpu = X86_CPU(cs);
+
+        if (!cpu->apic_state) {
+            cpu_interrupt(cs, CPU_INTERRUPT_NMI);
+        } else {
+            apic_deliver_nmi(cpu->apic_state);
+        }
+    }
+#else
+    nmi_monitor_handle(0, NULL);
+#endif
+}
+
 static const TypeInfo nmi_info = {
     .name          = TYPE_NMI,
     .parent        = TYPE_INTERFACE,
diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c
index 72b3a1d063..603ef50568 100644
--- a/hw/display/exynos4210_fimd.c
+++ b/hw/display/exynos4210_fimd.c
@@ -337,7 +337,7 @@ static inline void fimd_swap_data(unsigned int swap_ctl, uint64_t *data)
     if (swap_ctl & FIMD_WINCON_SWAP_BITS) {
         res = 0;
         for (i = 0; i < 64; i++) {
-            if (x & (1ULL << (64 - i))) {
+            if (x & (1ULL << (63 - i))) {
                 res |= (1ULL << i);
             }
         }
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index c1d2e704ec..454bfd7df5 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -71,7 +71,7 @@ void gic_update(GICState *s)
             || !(s->cpu_ctlr[cpu] & (GICC_CTLR_EN_GRP0 | GICC_CTLR_EN_GRP1))) {
             qemu_irq_lower(s->parent_irq[cpu]);
             qemu_irq_lower(s->parent_fiq[cpu]);
-            return;
+            continue;
         }
         best_prio = 0x100;
         best_irq = 1023;
diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c
index ac3ab39bea..d1fe6d58e8 100644
--- a/hw/sd/pxa2xx_mmci.c
+++ b/hw/sd/pxa2xx_mmci.c
@@ -48,7 +48,6 @@ struct PXA2xxMMCIState {
     int resp_len;
 
     int cmdreq;
-    int ac_width;
 };
 
 #define MMC_STRPCL	0x00	/* MMC Clock Start/Stop register */
@@ -215,7 +214,7 @@ static void pxa2xx_mmci_wakequeues(PXA2xxMMCIState *s)
     pxa2xx_mmci_fifo_update(s);
 }
 
-static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset)
+static uint64_t pxa2xx_mmci_read(void *opaque, hwaddr offset, unsigned size)
 {
     PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
     uint32_t ret;
@@ -257,8 +256,8 @@ static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset)
         return 0;
     case MMC_RXFIFO:
         ret = 0;
-        while (s->ac_width -- && s->rx_len) {
-            ret |= s->rx_fifo[s->rx_start ++] << (s->ac_width << 3);
+        while (size-- && s->rx_len) {
+            ret |= s->rx_fifo[s->rx_start++] << (size << 3);
             s->rx_start &= 0x1f;
             s->rx_len --;
         }
@@ -277,7 +276,7 @@ static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset)
 }
 
 static void pxa2xx_mmci_write(void *opaque,
-                hwaddr offset, uint32_t value)
+                              hwaddr offset, uint64_t value, unsigned size)
 {
     PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
 
@@ -370,9 +369,9 @@ static void pxa2xx_mmci_write(void *opaque,
         break;
 
     case MMC_TXFIFO:
-        while (s->ac_width -- && s->tx_len < 0x20)
+        while (size-- && s->tx_len < 0x20)
             s->tx_fifo[(s->tx_start + (s->tx_len ++)) & 0x1f] =
-                    (value >> (s->ac_width << 3)) & 0xff;
+                    (value >> (size << 3)) & 0xff;
         s->intreq &= ~INT_TXFIFO_REQ;
         pxa2xx_mmci_fifo_update(s);
         break;
@@ -386,60 +385,9 @@ static void pxa2xx_mmci_write(void *opaque,
     }
 }
 
-static uint32_t pxa2xx_mmci_readb(void *opaque, hwaddr offset)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    s->ac_width = 1;
-    return pxa2xx_mmci_read(opaque, offset);
-}
-
-static uint32_t pxa2xx_mmci_readh(void *opaque, hwaddr offset)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    s->ac_width = 2;
-    return pxa2xx_mmci_read(opaque, offset);
-}
-
-static uint32_t pxa2xx_mmci_readw(void *opaque, hwaddr offset)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    s->ac_width = 4;
-    return pxa2xx_mmci_read(opaque, offset);
-}
-
-static void pxa2xx_mmci_writeb(void *opaque,
-                hwaddr offset, uint32_t value)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    s->ac_width = 1;
-    pxa2xx_mmci_write(opaque, offset, value);
-}
-
-static void pxa2xx_mmci_writeh(void *opaque,
-                hwaddr offset, uint32_t value)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    s->ac_width = 2;
-    pxa2xx_mmci_write(opaque, offset, value);
-}
-
-static void pxa2xx_mmci_writew(void *opaque,
-                hwaddr offset, uint32_t value)
-{
-    PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
-    s->ac_width = 4;
-    pxa2xx_mmci_write(opaque, offset, value);
-}
-
 static const MemoryRegionOps pxa2xx_mmci_ops = {
-    .old_mmio = {
-        .read = { pxa2xx_mmci_readb,
-                  pxa2xx_mmci_readh,
-                  pxa2xx_mmci_readw, },
-        .write = { pxa2xx_mmci_writeb,
-                   pxa2xx_mmci_writeh,
-                   pxa2xx_mmci_writew, },
-    },
+    .read = pxa2xx_mmci_read,
+    .write = pxa2xx_mmci_write,
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs
index 4b0374a555..72e3ffd93c 100644
--- a/hw/watchdog/Makefile.objs
+++ b/hw/watchdog/Makefile.objs
@@ -1,3 +1,4 @@
 common-obj-y += watchdog.o
 common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o
 common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o
+common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o
diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c
index 54440c91c5..8d4b0eeeb0 100644
--- a/hw/watchdog/watchdog.c
+++ b/hw/watchdog/watchdog.c
@@ -27,6 +27,7 @@
 #include "sysemu/sysemu.h"
 #include "sysemu/watchdog.h"
 #include "qapi-event.h"
+#include "hw/nmi.h"
 
 /* Possible values for action parameter. */
 #define WDT_RESET        1	/* Hard reset. */
@@ -35,6 +36,7 @@
 #define WDT_PAUSE        4	/* Pause. */
 #define WDT_DEBUG        5	/* Prints a message and continues running. */
 #define WDT_NONE         6	/* Do nothing. */
+#define WDT_NMI          7	/* Inject nmi into the guest */
 
 static int watchdog_action = WDT_RESET;
 static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
@@ -95,6 +97,8 @@ int select_watchdog_action(const char *p)
         watchdog_action = WDT_DEBUG;
     else if (strcasecmp(p, "none") == 0)
         watchdog_action = WDT_NONE;
+    else if (strcasecmp(p, "inject-nmi") == 0)
+        watchdog_action = WDT_NMI;
     else
         return -1;
 
@@ -138,5 +142,11 @@ void watchdog_perform_action(void)
     case WDT_NONE:
         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_NONE, &error_abort);
         break;
+
+    case WDT_NMI:
+        qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_INJECT_NMI,
+                                 &error_abort);
+        inject_nmi();
+        break;
     }
 }
diff --git a/hw/watchdog/wdt_diag288.c b/hw/watchdog/wdt_diag288.c
new file mode 100644
index 0000000000..1185e0681c
--- /dev/null
+++ b/hw/watchdog/wdt_diag288.c
@@ -0,0 +1,122 @@
+/*
+ * watchdog device diag288 support
+ *
+ * Copyright IBM, Corp. 2015
+ *
+ * Authors:
+ *  Xu Wang <gesaint@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at your
+ * option) any later version.  See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "sysemu/watchdog.h"
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "hw/watchdog/wdt_diag288.h"
+
+static WatchdogTimerModel model = {
+    .wdt_name = TYPE_WDT_DIAG288,
+    .wdt_description = "diag288 device for s390x platform",
+};
+
+static const VMStateDescription vmstate_diag288 = {
+    .name = "vmstate_diag288",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_TIMER_PTR(timer, DIAG288State),
+        VMSTATE_BOOL(enabled, DIAG288State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void wdt_diag288_reset(DeviceState *dev)
+{
+    DIAG288State *diag288 = DIAG288(dev);
+
+    diag288->enabled = false;
+    timer_del(diag288->timer);
+}
+
+static void diag288_timer_expired(void *dev)
+{
+    qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
+    watchdog_perform_action();
+    wdt_diag288_reset(dev);
+}
+
+static int wdt_diag288_handle_timer(DIAG288State *diag288,
+                                     uint64_t func, uint64_t timeout)
+{
+    switch (func) {
+    case WDT_DIAG288_INIT:
+        diag288->enabled = true;
+        /* fall through */
+    case WDT_DIAG288_CHANGE:
+        if (!diag288->enabled) {
+            return -1;
+        }
+        timer_mod(diag288->timer,
+                  qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+                  timeout * get_ticks_per_sec());
+        break;
+    case WDT_DIAG288_CANCEL:
+        if (!diag288->enabled) {
+            return -1;
+        }
+        diag288->enabled = false;
+        timer_del(diag288->timer);
+        break;
+    default:
+        return -1;
+    }
+
+    return 0;
+}
+
+static void wdt_diag288_realize(DeviceState *dev, Error **errp)
+{
+    DIAG288State *diag288 = DIAG288(dev);
+
+    diag288->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, diag288_timer_expired,
+                                  dev);
+}
+
+static void wdt_diag288_unrealize(DeviceState *dev, Error **errp)
+{
+    DIAG288State *diag288 = DIAG288(dev);
+
+    timer_del(diag288->timer);
+    timer_free(diag288->timer);
+}
+
+static void wdt_diag288_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    DIAG288Class *diag288 = DIAG288_CLASS(klass);
+
+    dc->realize = wdt_diag288_realize;
+    dc->unrealize = wdt_diag288_unrealize;
+    dc->reset = wdt_diag288_reset;
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+    dc->vmsd = &vmstate_diag288;
+    diag288->handle_timer = wdt_diag288_handle_timer;
+}
+
+static const TypeInfo wdt_diag288_info = {
+    .class_init = wdt_diag288_class_init,
+    .parent = TYPE_DEVICE,
+    .name  = TYPE_WDT_DIAG288,
+    .instance_size  = sizeof(DIAG288State),
+    .class_size = sizeof(DIAG288Class),
+};
+
+static void wdt_diag288_register_types(void)
+{
+    watchdog_add_model(&model);
+    type_register_static(&wdt_diag288_info);
+}
+
+type_init(wdt_diag288_register_types)
diff --git a/include/block/block.h b/include/block/block.h
index f7680b6e68..07bb724f7d 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -12,6 +12,7 @@
 /* block.c */
 typedef struct BlockDriver BlockDriver;
 typedef struct BlockJob BlockJob;
+typedef struct BdrvChildRole BdrvChildRole;
 
 typedef struct BlockDriverInfo {
     /* in bytes, 0 if irrelevant */
@@ -90,6 +91,14 @@ typedef struct HDGeometry {
 
 #define BDRV_O_CACHE_MASK  (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
 
+
+/* Option names of options parsed by the block layer */
+
+#define BDRV_OPT_CACHE_WB       "cache.writeback"
+#define BDRV_OPT_CACHE_DIRECT   "cache.direct"
+#define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush"
+
+
 #define BDRV_SECTOR_BITS   9
 #define BDRV_SECTOR_SIZE   (1ULL << BDRV_SECTOR_BITS)
 #define BDRV_SECTOR_MASK   ~(BDRV_SECTOR_SIZE - 1)
@@ -173,8 +182,9 @@ void bdrv_stats_print(Monitor *mon, const QObject *data);
 void bdrv_info_stats(Monitor *mon, QObject **ret_data);
 
 /* disk I/O throttling */
-void bdrv_io_limits_enable(BlockDriverState *bs);
+void bdrv_io_limits_enable(BlockDriverState *bs, const char *group);
 void bdrv_io_limits_disable(BlockDriverState *bs);
+void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group);
 
 void bdrv_init(void);
 void bdrv_init_with_whitelist(void);
@@ -195,7 +205,8 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
 int bdrv_parse_cache_flags(const char *mode, int *flags);
 int bdrv_parse_discard_flags(const char *mode, int *flags);
 int bdrv_open_image(BlockDriverState **pbs, const char *filename,
-                    QDict *options, const char *bdref_key, int flags,
+                    QDict *options, const char *bdref_key,
+                    BlockDriverState* parent, const BdrvChildRole *child_role,
                     bool allow_none, Error **errp);
 void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd);
 int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index f004378d58..888ec09e96 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -330,6 +330,19 @@ typedef struct BdrvAioNotifier {
     QLIST_ENTRY(BdrvAioNotifier) list;
 } BdrvAioNotifier;
 
+struct BdrvChildRole {
+    int (*inherit_flags)(int parent_flags);
+};
+
+extern const BdrvChildRole child_file;
+extern const BdrvChildRole child_format;
+
+typedef struct BdrvChild {
+    BlockDriverState *bs;
+    const BdrvChildRole *role;
+    QLIST_ENTRY(BdrvChild) next;
+} BdrvChild;
+
 /*
  * Note: the function bdrv_append() copies and swaps contents of
  * BlockDriverStates, so if you add new fields to this struct, please
@@ -379,9 +392,14 @@ struct BlockDriverState {
     unsigned int serialising_in_flight;
 
     /* I/O throttling */
-    ThrottleState throttle_state;
     CoQueue      throttled_reqs[2];
     bool         io_limits_enabled;
+    /* The following fields are protected by the ThrottleGroup lock.
+     * See the ThrottleGroup documentation for details. */
+    ThrottleState *throttle_state;
+    ThrottleTimers throttle_timers;
+    unsigned       pending_reqs[2];
+    QLIST_ENTRY(BlockDriverState) round_robin;
 
     /* I/O stats (display with "info blockstats"). */
     BlockAcctStats stats;
@@ -424,6 +442,12 @@ struct BlockDriverState {
     /* long-running background operation */
     BlockJob *job;
 
+    /* The node that this node inherited default options from (and a reopen on
+     * which can affect this node by changing these defaults). This is always a
+     * parent node of this node. */
+    BlockDriverState *inherits_from;
+    QLIST_HEAD(, BdrvChild) children;
+
     QDict *options;
     BlockdevDetectZeroesOptions detect_zeroes;
 
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
new file mode 100644
index 0000000000..fab113f6d1
--- /dev/null
+++ b/include/block/throttle-groups.h
@@ -0,0 +1,46 @@
+/*
+ * QEMU block throttling group infrastructure
+ *
+ * Copyright (C) Nodalink, EURL. 2014
+ * Copyright (C) Igalia, S.L. 2015
+ *
+ * Authors:
+ *   Benoît Canet <benoit.canet@nodalink.com>
+ *   Alberto Garcia <berto@igalia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef THROTTLE_GROUPS_H
+#define THROTTLE_GROUPS_H
+
+#include "qemu/throttle.h"
+#include "block/block_int.h"
+
+const char *throttle_group_get_name(BlockDriverState *bs);
+
+void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg);
+void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg);
+
+void throttle_group_register_bs(BlockDriverState *bs, const char *groupname);
+void throttle_group_unregister_bs(BlockDriverState *bs);
+
+void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs,
+                                                        unsigned int bytes,
+                                                        bool is_write);
+
+void throttle_group_lock(BlockDriverState *bs);
+void throttle_group_unlock(BlockDriverState *bs);
+
+#endif
diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h
index 59cf277434..7b4bfb7494 100644
--- a/include/hw/acpi/acpi-defs.h
+++ b/include/hw/acpi/acpi-defs.h
@@ -197,6 +197,38 @@ enum {
 };
 
 /*
+ * Serial Port Console Redirection Table (SPCR), Rev. 1.02
+ *
+ * For .interface_type see Debug Port Table 2 (DBG2) serial port
+ * subtypes in Table 3, Rev. May 22, 2012
+ */
+struct AcpiSerialPortConsoleRedirection {
+    ACPI_TABLE_HEADER_DEF
+    uint8_t  interface_type;
+    uint8_t  reserved1[3];
+    struct AcpiGenericAddress base_address;
+    uint8_t  interrupt_types;
+    uint8_t  irq;
+    uint32_t gsi;
+    uint8_t  baud;
+    uint8_t  parity;
+    uint8_t  stopbits;
+    uint8_t  flowctrl;
+    uint8_t  term_type;
+    uint8_t  reserved2;
+    uint16_t pci_device_id;
+    uint16_t pci_vendor_id;
+    uint8_t  pci_bus;
+    uint8_t  pci_slot;
+    uint8_t  pci_func;
+    uint32_t pci_flags;
+    uint8_t  pci_seg;
+    uint32_t reserved3;
+} QEMU_PACKED;
+typedef struct AcpiSerialPortConsoleRedirection
+               AcpiSerialPortConsoleRedirection;
+
+/*
  * ACPI 1.0 Root System Description Table (RSDT)
  */
 struct AcpiRsdtDescriptorRev1
diff --git a/include/hw/nmi.h b/include/hw/nmi.h
index b541772e1d..f4cec6257d 100644
--- a/include/hw/nmi.h
+++ b/include/hw/nmi.h
@@ -45,5 +45,6 @@ typedef struct NMIClass {
 } NMIClass;
 
 void nmi_monitor_handle(int cpu_index, Error **errp);
+void inject_nmi(void);
 
 #endif /* NMI_H */
diff --git a/include/hw/watchdog/wdt_diag288.h b/include/hw/watchdog/wdt_diag288.h
new file mode 100644
index 0000000000..7f3fd450dc
--- /dev/null
+++ b/include/hw/watchdog/wdt_diag288.h
@@ -0,0 +1,36 @@
+#ifndef WDT_DIAG288_H
+#define WDT_DIAG288_H
+
+#include "hw/qdev.h"
+
+#define TYPE_WDT_DIAG288 "diag288"
+#define DIAG288(obj) \
+    OBJECT_CHECK(DIAG288State, (obj), TYPE_WDT_DIAG288)
+#define DIAG288_CLASS(klass) \
+    OBJECT_CLASS_CHECK(DIAG288Class, (klass), TYPE_WDT_DIAG288)
+#define DIAG288_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(DIAG288Class, (obj), TYPE_WDT_DIAG288)
+
+#define WDT_DIAG288_INIT      0
+#define WDT_DIAG288_CHANGE    1
+#define WDT_DIAG288_CANCEL    2
+
+typedef struct DIAG288State {
+    /*< private >*/
+    DeviceState parent_obj;
+    QEMUTimer *timer;
+    bool enabled;
+
+    /*< public >*/
+} DIAG288State;
+
+typedef struct DIAG288Class {
+    /*< private >*/
+    DeviceClass parent_class;
+
+    /*< public >*/
+    int (*handle_timer)(DIAG288State *dev,
+                        uint64_t func, uint64_t timeout);
+} DIAG288Class;
+
+#endif  /* WDT_DIAG288_H */
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 57f8394a94..88644ceda7 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -8,7 +8,6 @@
 #include "qemu/readline.h"
 
 extern Monitor *cur_mon;
-extern Monitor *default_mon;
 
 /* flags for monitor_init */
 #define MONITOR_IS_DEFAULT    0x01
diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
index d68f4eb4d5..9fbf68ee0c 100644
--- a/include/qapi/qmp/qdict.h
+++ b/include/qapi/qmp/qdict.h
@@ -65,11 +65,15 @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key,
 int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value);
 const char *qdict_get_try_str(const QDict *qdict, const char *key);
 
+void qdict_copy_default(QDict *dst, QDict *src, const char *key);
+void qdict_set_default_str(QDict *dst, const char *key, const char *val);
+
 QDict *qdict_clone_shallow(const QDict *src);
 void qdict_flatten(QDict *qdict);
 
 void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
 void qdict_array_split(QDict *src, QList **dst);
+int qdict_array_entries(QDict *src, const char *subqdict);
 
 void qdict_join(QDict *dest, QDict *src, bool overwrite);
 
diff --git a/include/qemu/queue.h b/include/qemu/queue.h
index f781aa20a8..a8d3cb8e63 100644
--- a/include/qemu/queue.h
+++ b/include/qemu/queue.h
@@ -117,6 +117,12 @@ struct {                                                                \
         }                                                               \
 } while (/*CONSTCOND*/0)
 
+#define QLIST_FIX_HEAD_PTR(head, field) do {                            \
+        if ((head)->lh_first != NULL) {                                 \
+            (head)->lh_first->field.le_prev = &(head)->lh_first;        \
+        }                                                               \
+} while (/*CONSTCOND*/0)
+
 #define QLIST_INSERT_AFTER(listelm, elm, field) do {                    \
         if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)  \
                 (listelm)->field.le_next->field.le_prev =               \
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index b890613a9c..5af76f0ff4 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -1,10 +1,12 @@
 /*
  * QEMU throttling infrastructure
  *
- * Copyright (C) Nodalink, SARL. 2013
+ * Copyright (C) Nodalink, EURL. 2013-2014
+ * Copyright (C) Igalia, S.L. 2015
  *
- * Author:
- *   Benoît Canet <benoit.canet@irqsave.net>
+ * Authors:
+ *   Benoît Canet <benoit.canet@nodalink.com>
+ *   Alberto Garcia <berto@igalia.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -65,14 +67,17 @@ typedef struct ThrottleConfig {
 typedef struct ThrottleState {
     ThrottleConfig cfg;       /* configuration */
     int64_t previous_leak;    /* timestamp of the last leak done */
-    QEMUTimer * timers[2];    /* timers used to do the throttling */
+} ThrottleState;
+
+typedef struct ThrottleTimers {
+    QEMUTimer *timers[2];     /* timers used to do the throttling */
     QEMUClockType clock_type; /* the clock used */
 
     /* Callbacks */
     QEMUTimerCB *read_timer_cb;
     QEMUTimerCB *write_timer_cb;
     void *timer_opaque;
-} ThrottleState;
+} ThrottleTimers;
 
 /* operations on single leaky buckets */
 void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta);
@@ -86,20 +91,23 @@ bool throttle_compute_timer(ThrottleState *ts,
                             int64_t *next_timestamp);
 
 /* init/destroy cycle */
-void throttle_init(ThrottleState *ts,
-                   AioContext *aio_context,
-                   QEMUClockType clock_type,
-                   void (read_timer)(void *),
-                   void (write_timer)(void *),
-                   void *timer_opaque);
+void throttle_init(ThrottleState *ts);
+
+void throttle_timers_init(ThrottleTimers *tt,
+                          AioContext *aio_context,
+                          QEMUClockType clock_type,
+                          QEMUTimerCB *read_timer_cb,
+                          QEMUTimerCB *write_timer_cb,
+                          void *timer_opaque);
 
-void throttle_destroy(ThrottleState *ts);
+void throttle_timers_destroy(ThrottleTimers *tt);
 
-void throttle_detach_aio_context(ThrottleState *ts);
+void throttle_timers_detach_aio_context(ThrottleTimers *tt);
 
-void throttle_attach_aio_context(ThrottleState *ts, AioContext *new_context);
+void throttle_timers_attach_aio_context(ThrottleTimers *tt,
+                                        AioContext *new_context);
 
-bool throttle_have_timer(ThrottleState *ts);
+bool throttle_timers_are_initialized(ThrottleTimers *tt);
 
 /* configuration */
 bool throttle_enabled(ThrottleConfig *cfg);
@@ -108,12 +116,16 @@ bool throttle_conflicting(ThrottleConfig *cfg);
 
 bool throttle_is_valid(ThrottleConfig *cfg);
 
-void throttle_config(ThrottleState *ts, ThrottleConfig *cfg);
+void throttle_config(ThrottleState *ts,
+                     ThrottleTimers *tt,
+                     ThrottleConfig *cfg);
 
 void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg);
 
 /* usage */
-bool throttle_schedule_timer(ThrottleState *ts, bool is_write);
+bool throttle_schedule_timer(ThrottleState *ts,
+                             ThrottleTimers *tt,
+                             bool is_write);
 
 void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
 
diff --git a/iothread.c b/iothread.c
index 0416fc4268..878a594ef4 100644
--- a/iothread.c
+++ b/iothread.c
@@ -31,14 +31,21 @@ typedef ObjectClass IOThreadClass;
 static void *iothread_run(void *opaque)
 {
     IOThread *iothread = opaque;
+    bool blocking;
 
     qemu_mutex_lock(&iothread->init_done_lock);
     iothread->thread_id = qemu_get_thread_id();
     qemu_cond_signal(&iothread->init_done_cond);
     qemu_mutex_unlock(&iothread->init_done_lock);
 
-    while (!atomic_read(&iothread->stopping)) {
-        aio_poll(iothread->ctx, true);
+    while (!iothread->stopping) {
+        aio_context_acquire(iothread->ctx);
+        blocking = true;
+        while (!iothread->stopping && aio_poll(iothread->ctx, blocking)) {
+            /* Progress was made, keep going */
+            blocking = false;
+        }
+        aio_context_release(iothread->ctx);
     }
     return NULL;
 }
diff --git a/monitor.c b/monitor.c
index 6a4642493a..8e1a2e85b8 100644
--- a/monitor.c
+++ b/monitor.c
@@ -226,7 +226,6 @@ static mon_cmd_t info_cmds[];
 static const mon_cmd_t qmp_cmds[];
 
 Monitor *cur_mon;
-Monitor *default_mon;
 
 static void monitor_command_cb(void *opaque, const char *cmdline,
                                void *readline_opaque);
@@ -5298,9 +5297,6 @@ void monitor_init(CharDriverState *chr, int flags)
     qemu_mutex_lock(&monitor_lock);
     QLIST_INSERT_HEAD(&mon_list, mon, entry);
     qemu_mutex_unlock(&monitor_lock);
-
-    if (!default_mon || (flags & MONITOR_IS_DEFAULT))
-        default_mon = mon;
 }
 
 static void bdrv_password_cb(void *opaque, const char *password,
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index 009bb8de1c..746603a315 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -10,7 +10,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
 .PHONY : all clean build-all
 
 OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o
-CFLAGS += -fPIE -fno-stack-protector -ffreestanding
+CFLAGS += -fPIE -fno-stack-protector -ffreestanding -fno-delete-null-pointer-checks
 LDFLAGS += -Wl,-pie -nostdlib
 
 build-all: s390-ccw.img
diff --git a/qapi-schema.json b/qapi-schema.json
index bcc604b813..106008cdeb 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3746,10 +3746,14 @@
 #
 # @none: nothing is done
 #
+# @inject-nmi: a non-maskable interrupt is injected into the first VCPU (all
+#              VCPUS on x86) (since 2.4)
+#
 # Since: 2.1
 ##
 { 'enum': 'WatchdogExpirationAction',
-  'data': [ 'reset', 'shutdown', 'poweroff', 'pause', 'debug', 'none' ] }
+  'data': [ 'reset', 'shutdown', 'poweroff', 'pause', 'debug', 'none',
+            'inject-nmi' ] }
 
 ##
 # @IoOperationType
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 8411d4f83a..afa9d3d1f3 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -259,6 +259,8 @@
 #
 # @iops_size: #optional an I/O size in bytes (Since 1.7)
 #
+# @group: #optional throttle group name (Since 2.4)
+#
 # @cache: the cache mode used for the block device (since: 2.3)
 #
 # @write_threshold: configured write threshold for the device.
@@ -278,7 +280,7 @@
             '*bps_max': 'int', '*bps_rd_max': 'int',
             '*bps_wr_max': 'int', '*iops_max': 'int',
             '*iops_rd_max': 'int', '*iops_wr_max': 'int',
-            '*iops_size': 'int', 'cache': 'BlockdevCacheInfo',
+            '*iops_size': 'int', '*group': 'str', 'cache': 'BlockdevCacheInfo',
             'write_threshold': 'int' } }
 
 ##
@@ -1062,6 +1064,27 @@
 #
 # Change I/O throttle limits for a block drive.
 #
+# Since QEMU 2.4, each device with I/O limits is member of a throttle
+# group.
+#
+# If two or more devices are members of the same group, the limits
+# will apply to the combined I/O of the whole group in a round-robin
+# fashion. Therefore, setting new I/O limits to a device will affect
+# the whole group.
+#
+# The name of the group can be specified using the 'group' parameter.
+# If the parameter is unset, it is assumed to be the current group of
+# that device. If it's not in any group yet, the name of the device
+# will be used as the name for its group.
+#
+# The 'group' parameter can also be used to move a device to a
+# different group. In this case the limits specified in the parameters
+# will be applied to the new group only.
+#
+# I/O limits can be disabled by setting all of them to 0. In this case
+# the device will be removed from its group and the rest of its
+# members will no be affected. The 'group' parameter is ignored.
+#
 # @device: The name of the device
 #
 # @bps: total throughput limit in bytes per second
@@ -1090,6 +1113,8 @@
 #
 # @iops_size: #optional an I/O size in bytes (Since 1.7)
 #
+# @group: #optional throttle group name (Since 2.4)
+#
 # Returns: Nothing on success
 #          If @device is not a valid block device, DeviceNotFound
 #
@@ -1101,7 +1126,7 @@
             '*bps_max': 'int', '*bps_rd_max': 'int',
             '*bps_wr_max': 'int', '*iops_max': 'int',
             '*iops_rd_max': 'int', '*iops_wr_max': 'int',
-            '*iops_size': 'int' } }
+            '*iops_size': 'int', '*group': 'str' } }
 
 ##
 # @block-stream:
diff --git a/qemu-options.hx b/qemu-options.hx
index 1d281f6818..bf1683e5b4 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -468,6 +468,7 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive,
     "       [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n"
     "       [[,iops_max=im]|[[,iops_rd_max=irm][,iops_wr_max=iwm]]]\n"
     "       [[,iops_size=is]]\n"
+    "       [[,group=g]]\n"
     "                use 'file' as a drive image\n", QEMU_ARCH_ALL)
 STEXI
 @item -drive @var{option}[,@var{option}[,@var{option}[,...]]]
@@ -3152,7 +3153,7 @@ when the shift value is high (how high depends on the host machine).
 ETEXI
 
 DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
-    "-watchdog i6300esb|ib700\n" \
+    "-watchdog model\n" \
     "                enable virtual hardware watchdog [default=none]\n",
     QEMU_ARCH_ALL)
 STEXI
@@ -3160,16 +3161,24 @@ STEXI
 @findex -watchdog
 Create a virtual hardware watchdog device.  Once enabled (by a guest
 action), the watchdog must be periodically polled by an agent inside
-the guest or else the guest will be restarted.
+the guest or else the guest will be restarted. Choose a model for
+which your guest has drivers.
 
-The @var{model} is the model of hardware watchdog to emulate.  Choices
-for model are: @code{ib700} (iBASE 700) which is a very simple ISA
-watchdog with a single timer, or @code{i6300esb} (Intel 6300ESB I/O
-controller hub) which is a much more featureful PCI-based dual-timer
-watchdog.  Choose a model for which your guest has drivers.
-
-Use @code{-watchdog help} to list available hardware models.  Only one
+The @var{model} is the model of hardware watchdog to emulate. Use
+@code{-watchdog help} to list available hardware models. Only one
 watchdog can be enabled for a guest.
+
+The following models may be available:
+@table @option
+@item ib700
+iBASE 700 is a very simple ISA watchdog with a single timer.
+@item i6300esb
+Intel 6300ESB I/O controller hub is a much more featureful PCI-based
+dual-timer watchdog.
+@item diag288
+A virtual watchdog for s390x backed by the diagnose 288 hypercall
+(currently KVM only).
+@end table
 ETEXI
 
 DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \
diff --git a/qmp-commands.hx b/qmp-commands.hx
index c97d0d7667..1db6524e97 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1853,7 +1853,7 @@ EQMP
 
     {
         .name       = "block_set_io_throttle",
-        .args_type  = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?",
+        .args_type  = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?,group:s?",
         .mhandler.cmd_new = qmp_marshal_input_block_set_io_throttle,
     },
 
@@ -1879,6 +1879,7 @@ Arguments:
 - "iops_rd_max":  read I/O operations max (json-int)
 - "iops_wr_max":  write I/O operations max (json-int)
 - "iops_size":  I/O size in bytes when limiting (json-int)
+- "group": throttle group name (json-string)
 
 Example:
 
diff --git a/qobject/qdict.c b/qobject/qdict.c
index ea239f082e..190791becf 100644
--- a/qobject/qdict.c
+++ b/qobject/qdict.c
@@ -477,6 +477,39 @@ static void qdict_destroy_obj(QObject *obj)
     g_free(qdict);
 }
 
+/**
+ * qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the
+ * value of 'key' in 'src' is copied there (and the refcount increased
+ * accordingly).
+ */
+void qdict_copy_default(QDict *dst, QDict *src, const char *key)
+{
+    QObject *val;
+
+    if (qdict_haskey(dst, key)) {
+        return;
+    }
+
+    val = qdict_get(src, key);
+    if (val) {
+        qobject_incref(val);
+        qdict_put_obj(dst, key, val);
+    }
+}
+
+/**
+ * qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a
+ * new QString initialised by 'val' is put there.
+ */
+void qdict_set_default_str(QDict *dst, const char *key, const char *val)
+{
+    if (qdict_haskey(dst, key)) {
+        return;
+    }
+
+    qdict_put(dst, key, qstring_from_str(val));
+}
+
 static void qdict_flatten_qdict(QDict *qdict, QDict *target,
                                 const char *prefix);
 
@@ -597,17 +630,21 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start)
     }
 }
 
-static bool qdict_has_prefixed_entries(const QDict *src, const char *start)
+static int qdict_count_prefixed_entries(const QDict *src, const char *start)
 {
     const QDictEntry *entry;
+    int count = 0;
 
     for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) {
         if (strstart(entry->key, start, NULL)) {
-            return true;
+            if (count == INT_MAX) {
+                return -ERANGE;
+            }
+            count++;
         }
     }
 
-    return false;
+    return count;
 }
 
 /**
@@ -646,7 +683,8 @@ void qdict_array_split(QDict *src, QList **dst)
         snprintf_ret = snprintf(prefix, 32, "%u.", i);
         assert(snprintf_ret < 32);
 
-        is_subqdict = qdict_has_prefixed_entries(src, prefix);
+        /* Overflow is the same as positive non-zero results */
+        is_subqdict = qdict_count_prefixed_entries(src, prefix);
 
         // There may be either a single subordinate object (named "%u") or
         // multiple objects (each with a key prefixed "%u."), but not both.
@@ -667,6 +705,71 @@ void qdict_array_split(QDict *src, QList **dst)
 }
 
 /**
+ * qdict_array_entries(): Returns the number of direct array entries if the
+ * sub-QDict of src specified by the prefix in subqdict (or src itself for
+ * prefix == "") is valid as an array, i.e. the length of the created list if
+ * the sub-QDict would become empty after calling qdict_array_split() on it. If
+ * the array is not valid, -EINVAL is returned.
+ */
+int qdict_array_entries(QDict *src, const char *subqdict)
+{
+    const QDictEntry *entry;
+    unsigned i;
+    unsigned entries = 0;
+    size_t subqdict_len = strlen(subqdict);
+
+    assert(!subqdict_len || subqdict[subqdict_len - 1] == '.');
+
+    /* qdict_array_split() loops until UINT_MAX, but as we want to return
+     * negative errors, we only have a signed return value here. Any additional
+     * entries will lead to -EINVAL. */
+    for (i = 0; i < INT_MAX; i++) {
+        QObject *subqobj;
+        int subqdict_entries;
+        size_t slen = 32 + subqdict_len;
+        char indexstr[slen], prefix[slen];
+        size_t snprintf_ret;
+
+        snprintf_ret = snprintf(indexstr, slen, "%s%u", subqdict, i);
+        assert(snprintf_ret < slen);
+
+        subqobj = qdict_get(src, indexstr);
+
+        snprintf_ret = snprintf(prefix, slen, "%s%u.", subqdict, i);
+        assert(snprintf_ret < slen);
+
+        subqdict_entries = qdict_count_prefixed_entries(src, prefix);
+        if (subqdict_entries < 0) {
+            return subqdict_entries;
+        }
+
+        /* There may be either a single subordinate object (named "%u") or
+         * multiple objects (each with a key prefixed "%u."), but not both. */
+        if (subqobj && subqdict_entries) {
+            return -EINVAL;
+        } else if (!subqobj && !subqdict_entries) {
+            break;
+        }
+
+        entries += subqdict_entries ? subqdict_entries : 1;
+    }
+
+    /* Consider everything handled that isn't part of the given sub-QDict */
+    for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) {
+        if (!strstart(qdict_entry_key(entry), subqdict, NULL)) {
+            entries++;
+        }
+    }
+
+    /* Anything left in the sub-QDict that wasn't handled? */
+    if (qdict_size(src) != entries) {
+        return -EINVAL;
+    }
+
+    return i;
+}
+
+/**
  * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all
  * elements from src to dest.
  *
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index ed5a6441bb..072aa9baa7 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -103,6 +103,9 @@ typedef struct ARMCPU {
     /* CPU has security extension */
     bool has_el3;
 
+    /* CPU has memory protection unit */
+    bool has_mpu;
+
     /* PSCI conduit used to invoke PSCI methods
      * 0 - disabled, 1 - smc, 2 - hvc
      */
@@ -116,6 +119,9 @@ typedef struct ARMCPU {
     /* KVM init features for this CPU */
     uint32_t kvm_init_features[7];
 
+    /* Uniprocessor system with MP extensions */
+    bool mp_is_up;
+
     /* The instance init functions for implementation-specific subclasses
      * set these fields to specify the implementation-dependent values of
      * various constant registers and reset values of non-constant
@@ -127,6 +133,7 @@ typedef struct ARMCPU {
      * prefix means a constant register.
      */
     uint32_t midr;
+    uint32_t revidr;
     uint32_t reset_fpsid;
     uint32_t mvfr0;
     uint32_t mvfr1;
@@ -159,6 +166,7 @@ typedef struct ARMCPU {
     uint64_t id_aa64mmfr1;
     uint32_t dbgdidr;
     uint32_t clidr;
+    uint64_t mp_affinity; /* MP ID without feature bits */
     /* The elements of this array are the CCSIDR values for each cache,
      * in the order L1DCache, L1ICache, L2DCache, L2ICache, etc.
      */
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 4a888ab47a..7496983b42 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -383,17 +383,29 @@ static inline void unset_feature(CPUARMState *env, int feature)
     env->features &= ~(1ULL << feature);
 }
 
+#define ARM_CPUS_PER_CLUSTER 8
+
 static void arm_cpu_initfn(Object *obj)
 {
     CPUState *cs = CPU(obj);
     ARMCPU *cpu = ARM_CPU(obj);
     static bool inited;
+    uint32_t Aff1, Aff0;
 
     cs->env_ptr = &cpu->env;
     cpu_exec_init(&cpu->env);
     cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal,
                                          g_free, g_free);
 
+    /* This cpu-id-to-MPIDR affinity is used only for TCG; KVM will override it.
+     * We don't support setting cluster ID ([16..23]) (known as Aff2
+     * in later ARM ARM versions), or any of the higher affinity level fields,
+     * so these bits always RAZ.
+     */
+    Aff1 = cs->cpu_index / ARM_CPUS_PER_CLUSTER;
+    Aff0 = cs->cpu_index % ARM_CPUS_PER_CLUSTER;
+    cpu->mp_affinity = (Aff1 << 8) | Aff0;
+
 #ifndef CONFIG_USER_ONLY
     /* Our inbound IRQ and FIQ lines */
     if (kvm_enabled()) {
@@ -442,6 +454,9 @@ static Property arm_cpu_rvbar_property =
 static Property arm_cpu_has_el3_property =
             DEFINE_PROP_BOOL("has_el3", ARMCPU, has_el3, true);
 
+static Property arm_cpu_has_mpu_property =
+            DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true);
+
 static void arm_cpu_post_init(Object *obj)
 {
     ARMCPU *cpu = ARM_CPU(obj);
@@ -469,6 +484,12 @@ static void arm_cpu_post_init(Object *obj)
         qdev_property_add_static(DEVICE(obj), &arm_cpu_has_el3_property,
                                  &error_abort);
     }
+
+    if (arm_feature(&cpu->env, ARM_FEATURE_MPU)) {
+        qdev_property_add_static(DEVICE(obj), &arm_cpu_has_mpu_property,
+                                 &error_abort);
+    }
+
 }
 
 static void arm_cpu_finalizefn(Object *obj)
@@ -533,6 +554,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
     if (arm_feature(env, ARM_FEATURE_CBAR_RO)) {
         set_feature(env, ARM_FEATURE_CBAR);
     }
+    if (arm_feature(env, ARM_FEATURE_THUMB2) &&
+        !arm_feature(env, ARM_FEATURE_M)) {
+        set_feature(env, ARM_FEATURE_THUMB_DSP);
+    }
 
     if (cpu->reset_hivecs) {
             cpu->reset_sctlr |= (1 << 13);
@@ -551,6 +576,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
         cpu->id_aa64pfr0 &= ~0xf000;
     }
 
+    if (!cpu->has_mpu) {
+        unset_feature(env, ARM_FEATURE_MPU);
+    }
+
     register_cp_regs_for_features(cpu);
     arm_cpu_register_gdb_regs_for_features(cpu);
 
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 21b5b8e538..c9d2330014 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -384,7 +384,6 @@ typedef struct CPUARMState {
         uint32_t control;
         int current_sp;
         int exception;
-        int pending_exception;
     } v7m;
 
     /* Information associated with an exception about to be taken:
@@ -890,6 +889,7 @@ enum arm_features {
     ARM_FEATURE_V8_SHA1, /* implements SHA1 part of v8 Crypto Extensions */
     ARM_FEATURE_V8_SHA256, /* implements SHA256 part of v8 Crypto Extensions */
     ARM_FEATURE_V8_PMULL, /* implements PMULL part of v8 Crypto Extensions */
+    ARM_FEATURE_THUMB_DSP, /* DSP insns supported in the Thumb encodings */
 };
 
 static inline int arm_feature(CPUARMState *env, int feature)
diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c
index bf7dd685f8..63c8b1cfa9 100644
--- a/target-arm/cpu64.c
+++ b/target-arm/cpu64.c
@@ -110,6 +110,7 @@ static void aarch64_a57_initfn(Object *obj)
     set_feature(&cpu->env, ARM_FEATURE_CRC);
     cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A57;
     cpu->midr = 0x411fd070;
+    cpu->revidr = 0x00000000;
     cpu->reset_fpsid = 0x41034070;
     cpu->mvfr0 = 0x10110222;
     cpu->mvfr1 = 0x12111111;
@@ -159,7 +160,9 @@ static void aarch64_a53_initfn(Object *obj)
     set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
     set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
     set_feature(&cpu->env, ARM_FEATURE_CRC);
+    cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A53;
     cpu->midr = 0x410fd034;
+    cpu->revidr = 0x00000000;
     cpu->reset_fpsid = 0x41034070;
     cpu->mvfr0 = 0x10110222;
     cpu->mvfr1 = 0x12111111;
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 3da0c0579c..00509b1382 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -12,10 +12,10 @@
 #include <zlib.h> /* For crc32 */
 
 #ifndef CONFIG_USER_ONLY
-static inline int get_phys_addr(CPUARMState *env, target_ulong address,
-                                int access_type, ARMMMUIdx mmu_idx,
-                                hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
-                                target_ulong *page_size);
+static inline bool get_phys_addr(CPUARMState *env, target_ulong address,
+                                 int access_type, ARMMMUIdx mmu_idx,
+                                 hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
+                                 target_ulong *page_size, uint32_t *fsr);
 
 /* Definitions for the PMCCNTR and PMCR registers */
 #define PMCRD   0x8
@@ -1495,19 +1495,20 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
     hwaddr phys_addr;
     target_ulong page_size;
     int prot;
-    int ret;
+    uint32_t fsr;
+    bool ret;
     uint64_t par64;
     MemTxAttrs attrs = {};
 
     ret = get_phys_addr(env, value, access_type, mmu_idx,
-                        &phys_addr, &attrs, &prot, &page_size);
+                        &phys_addr, &attrs, &prot, &page_size, &fsr);
     if (extended_addresses_enabled(env)) {
-        /* ret is a DFSR/IFSR value for the long descriptor
+        /* fsr is a DFSR/IFSR value for the long descriptor
          * translation table format, but with WnR always clear.
          * Convert it to a 64-bit PAR.
          */
         par64 = (1 << 11); /* LPAE bit always set */
-        if (ret == 0) {
+        if (!ret) {
             par64 |= phys_addr & ~0xfffULL;
             if (!attrs.secure) {
                 par64 |= (1 << 9); /* NS */
@@ -1515,18 +1516,18 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
             /* We don't set the ATTR or SH fields in the PAR. */
         } else {
             par64 |= 1; /* F */
-            par64 |= (ret & 0x3f) << 1; /* FS */
+            par64 |= (fsr & 0x3f) << 1; /* FS */
             /* Note that S2WLK and FSTAGE are always zero, because we don't
              * implement virtualization and therefore there can't be a stage 2
              * fault.
              */
         }
     } else {
-        /* ret is a DFSR/IFSR value for the short descriptor
+        /* fsr is a DFSR/IFSR value for the short descriptor
          * translation table format (with WnR always clear).
          * Convert it to a 32-bit PAR.
          */
-        if (ret == 0) {
+        if (!ret) {
             /* We do not set any attribute bits in the PAR */
             if (page_size == (1 << 24)
                 && arm_feature(env, ARM_FEATURE_V7)) {
@@ -1538,8 +1539,8 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
                 par64 |= (1 << 9); /* NS */
             }
         } else {
-            par64 = ((ret & (1 << 10)) >> 5) | ((ret & (1 << 12)) >> 6) |
-                    ((ret & 0xf) << 1) | 1;
+            par64 = ((fsr & (1 << 10)) >> 5) | ((fsr & (1 << 12)) >> 6) |
+                    ((fsr & 0xf) << 1) | 1;
         }
     }
     return par64;
@@ -1846,7 +1847,7 @@ static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri,
     raw_write(env, ri, value);
 }
 
-static const ARMCPRegInfo vmsa_cp_reginfo[] = {
+static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = {
     { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0,
       .access = PL1_RW, .type = ARM_CP_ALIAS,
       .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dfsr_s),
@@ -1856,6 +1857,18 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
       .access = PL1_RW, .resetvalue = 0,
       .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.ifsr_s),
                              offsetoflow32(CPUARMState, cp15.ifsr_ns) } },
+    { .name = "DFAR", .cp = 15, .opc1 = 0, .crn = 6, .crm = 0, .opc2 = 0,
+      .access = PL1_RW, .resetvalue = 0,
+      .bank_fieldoffsets = { offsetof(CPUARMState, cp15.dfar_s),
+                             offsetof(CPUARMState, cp15.dfar_ns) } },
+    { .name = "FAR_EL1", .state = ARM_CP_STATE_AA64,
+      .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
+      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]),
+      .resetvalue = 0, },
+    REGINFO_SENTINEL
+};
+
+static const ARMCPRegInfo vmsa_cp_reginfo[] = {
     { .name = "ESR_EL1", .state = ARM_CP_STATE_AA64,
       .opc0 = 3, .crn = 5, .crm = 2, .opc1 = 0, .opc2 = 0,
       .access = PL1_RW,
@@ -1880,14 +1893,6 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
       .resetfn = arm_cp_reset_ignore, .raw_writefn = vmsa_ttbcr_raw_write,
       .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tcr_el[3]),
                              offsetoflow32(CPUARMState, cp15.tcr_el[1])} },
-    { .name = "FAR_EL1", .state = ARM_CP_STATE_AA64,
-      .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
-      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]),
-      .resetvalue = 0, },
-    { .name = "DFAR", .cp = 15, .opc1 = 0, .crn = 6, .crm = 0, .opc2 = 0,
-      .access = PL1_RW, .resetvalue = 0,
-      .bank_fieldoffsets = { offsetof(CPUARMState, cp15.dfar_s),
-                             offsetof(CPUARMState, cp15.dfar_ns) } },
     REGINFO_SENTINEL
 };
 
@@ -2063,19 +2068,18 @@ static const ARMCPRegInfo strongarm_cp_reginfo[] = {
 
 static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri)
 {
-    CPUState *cs = CPU(arm_env_get_cpu(env));
-    uint32_t mpidr = cs->cpu_index;
-    /* We don't support setting cluster ID ([8..11]) (known as Aff1
-     * in later ARM ARM versions), or any of the higher affinity level fields,
-     * so these bits always RAZ.
-     */
+    ARMCPU *cpu = ARM_CPU(arm_env_get_cpu(env));
+    uint64_t mpidr = cpu->mp_affinity;
+
     if (arm_feature(env, ARM_FEATURE_V7MP)) {
         mpidr |= (1U << 31);
         /* Cores which are uniprocessor (non-coherent)
          * but still implement the MP extensions set
-         * bit 30. (For instance, A9UP.) However we do
-         * not currently model any of those cores.
+         * bit 30. (For instance, Cortex-R5).
          */
+        if (cpu->mp_is_up) {
+            mpidr |= (1u << 30);
+        }
     }
     return mpidr;
 }
@@ -3196,7 +3200,8 @@ void register_cp_regs_for_features(ARMCPU *cpu)
     if (arm_feature(env, ARM_FEATURE_V6K)) {
         define_arm_cp_regs(cpu, v6k_cp_reginfo);
     }
-    if (arm_feature(env, ARM_FEATURE_V7MP)) {
+    if (arm_feature(env, ARM_FEATURE_V7MP) &&
+        !arm_feature(env, ARM_FEATURE_MPU)) {
         define_arm_cp_regs(cpu, v7mp_cp_reginfo);
     }
     if (arm_feature(env, ARM_FEATURE_V7)) {
@@ -3348,6 +3353,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
         assert(!arm_feature(env, ARM_FEATURE_V6));
         define_arm_cp_regs(cpu, pmsav5_cp_reginfo);
     } else {
+        define_arm_cp_regs(cpu, vmsa_pmsa_cp_reginfo);
         define_arm_cp_regs(cpu, vmsa_cp_reginfo);
     }
     if (arm_feature(env, ARM_FEATURE_THUMB2EE)) {
@@ -3423,16 +3429,19 @@ void register_cp_regs_for_features(ARMCPU *cpu)
             REGINFO_SENTINEL
         };
         ARMCPRegInfo id_v8_midr_cp_reginfo[] = {
-            /* v8 MIDR -- the wildcard isn't necessary, and nor is the
-             * variable-MIDR TI925 behaviour. Instead we have a single
-             * (strictly speaking IMPDEF) alias of the MIDR, REVIDR.
-             */
             { .name = "MIDR_EL1", .state = ARM_CP_STATE_BOTH,
               .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 0,
               .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->midr },
+            /* crn = 0 op1 = 0 crm = 0 op2 = 4,7 : AArch32 aliases of MIDR */
+            { .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST,
+              .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 4,
+              .access = PL1_R, .resetvalue = cpu->midr },
+            { .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST,
+              .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 7,
+              .access = PL1_R, .resetvalue = cpu->midr },
             { .name = "REVIDR_EL1", .state = ARM_CP_STATE_BOTH,
               .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 6,
-              .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->midr },
+              .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->revidr },
             REGINFO_SENTINEL
         };
         ARMCPRegInfo id_cp_reginfo[] = {
@@ -3448,11 +3457,14 @@ void register_cp_regs_for_features(ARMCPU *cpu)
             { .name = "TCMTR",
               .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 2,
               .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
-            { .name = "TLBTR",
-              .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3,
-              .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
             REGINFO_SENTINEL
         };
+        /* TLBTR is specific to VMSA */
+        ARMCPRegInfo id_tlbtr_reginfo = {
+              .name = "TLBTR",
+              .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3,
+              .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0,
+        };
         ARMCPRegInfo crn0_wi_reginfo = {
             .name = "CRN0_WI", .cp = 15, .crn = 0, .crm = CP_ANY,
             .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_W,
@@ -3474,6 +3486,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
             for (r = id_cp_reginfo; r->type != ARM_CP_SENTINEL; r++) {
                 r->access = PL1_RW;
             }
+            id_tlbtr_reginfo.access = PL1_RW;
         }
         if (arm_feature(env, ARM_FEATURE_V8)) {
             define_arm_cp_regs(cpu, id_v8_midr_cp_reginfo);
@@ -3481,6 +3494,9 @@ void register_cp_regs_for_features(ARMCPU *cpu)
             define_arm_cp_regs(cpu, id_pre_v8_midr_cp_reginfo);
         }
         define_arm_cp_regs(cpu, id_cp_reginfo);
+        if (!arm_feature(env, ARM_FEATURE_MPU)) {
+            define_one_arm_cp_reg(cpu, &id_tlbtr_reginfo);
+        }
     }
 
     if (arm_feature(env, ARM_FEATURE_MPIDR)) {
@@ -5229,9 +5245,10 @@ static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure)
     return address_space_ldq(cs->as, addr, attrs, NULL);
 }
 
-static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type,
-                            ARMMMUIdx mmu_idx, hwaddr *phys_ptr,
-                            int *prot, target_ulong *page_size)
+static bool get_phys_addr_v5(CPUARMState *env, uint32_t address,
+                             int access_type, ARMMMUIdx mmu_idx,
+                             hwaddr *phys_ptr, int *prot,
+                             target_ulong *page_size, uint32_t *fsr)
 {
     CPUState *cs = CPU(arm_env_get_cpu(env));
     int code;
@@ -5302,20 +5319,25 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type,
             ap = (desc >> (4 + ((address >> 9) & 6))) & 3;
             *page_size = 0x1000;
             break;
-        case 3: /* 1k page.  */
+        case 3: /* 1k page, or ARMv6/XScale "extended small (4k) page" */
             if (type == 1) {
-                if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+                /* ARMv6/XScale extended small page format */
+                if (arm_feature(env, ARM_FEATURE_XSCALE)
+                    || arm_feature(env, ARM_FEATURE_V6)) {
                     phys_addr = (desc & 0xfffff000) | (address & 0xfff);
+                    *page_size = 0x1000;
                 } else {
-                    /* Page translation fault.  */
+                    /* UNPREDICTABLE in ARMv5; we choose to take a
+                     * page translation fault.
+                     */
                     code = 7;
                     goto do_fault;
                 }
             } else {
                 phys_addr = (desc & 0xfffffc00) | (address & 0x3ff);
+                *page_size = 0x400;
             }
             ap = (desc >> 4) & 3;
-            *page_size = 0x400;
             break;
         default:
             /* Never happens, but compiler isn't smart enough to tell.  */
@@ -5330,15 +5352,16 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type,
         goto do_fault;
     }
     *phys_ptr = phys_addr;
-    return 0;
+    return false;
 do_fault:
-    return code | (domain << 4);
+    *fsr = code | (domain << 4);
+    return true;
 }
 
-static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
-                            ARMMMUIdx mmu_idx, hwaddr *phys_ptr,
-                            MemTxAttrs *attrs,
-                            int *prot, target_ulong *page_size)
+static bool get_phys_addr_v6(CPUARMState *env, uint32_t address,
+                             int access_type, ARMMMUIdx mmu_idx,
+                             hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
+                             target_ulong *page_size, uint32_t *fsr)
 {
     CPUState *cs = CPU(arm_env_get_cpu(env));
     int code;
@@ -5392,6 +5415,8 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
         if (desc & (1 << 18)) {
             /* Supersection.  */
             phys_addr = (desc & 0xff000000) | (address & 0x00ffffff);
+            phys_addr |= (uint64_t)extract32(desc, 20, 4) << 32;
+            phys_addr |= (uint64_t)extract32(desc, 5, 4) << 36;
             *page_size = 0x1000000;
         } else {
             /* Section.  */
@@ -5469,9 +5494,10 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
         attrs->secure = false;
     }
     *phys_ptr = phys_addr;
-    return 0;
+    return false;
 do_fault:
-    return code | (domain << 4);
+    *fsr = code | (domain << 4);
+    return true;
 }
 
 /* Fault type for long-descriptor MMU fault reporting; this corresponds
@@ -5483,10 +5509,10 @@ typedef enum {
     permission_fault = 3,
 } MMUFaultType;
 
-static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
-                              int access_type, ARMMMUIdx mmu_idx,
-                              hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot,
-                              target_ulong *page_size_ptr)
+static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
+                               int access_type, ARMMMUIdx mmu_idx,
+                               hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot,
+                               target_ulong *page_size_ptr, uint32_t *fsr)
 {
     CPUState *cs = CPU(arm_env_get_cpu(env));
     /* Read an LPAE long-descriptor translation table. */
@@ -5725,16 +5751,17 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
     }
     *phys_ptr = descaddr;
     *page_size_ptr = page_size;
-    return 0;
+    return false;
 
 do_fault:
     /* Long-descriptor format IFSR/DFSR value */
-    return (1 << 9) | (fault_type << 2) | level;
+    *fsr = (1 << 9) | (fault_type << 2) | level;
+    return true;
 }
 
-static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
-                             int access_type, ARMMMUIdx mmu_idx,
-                             hwaddr *phys_ptr, int *prot)
+static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address,
+                                 int access_type, ARMMMUIdx mmu_idx,
+                                 hwaddr *phys_ptr, int *prot, uint32_t *fsr)
 {
     int n;
     uint32_t mask;
@@ -5756,7 +5783,8 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
         }
     }
     if (n < 0) {
-        return 2;
+        *fsr = 2;
+        return true;
     }
 
     if (access_type == 2) {
@@ -5767,10 +5795,12 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
     mask = (mask >> (n * 4)) & 0xf;
     switch (mask) {
     case 0:
-        return 1;
+        *fsr = 1;
+        return true;
     case 1:
         if (is_user) {
-            return 1;
+            *fsr = 1;
+            return true;
         }
         *prot = PAGE_READ | PAGE_WRITE;
         break;
@@ -5785,7 +5815,8 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
         break;
     case 5:
         if (is_user) {
-            return 1;
+            *fsr = 1;
+            return true;
         }
         *prot = PAGE_READ;
         break;
@@ -5794,10 +5825,11 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
         break;
     default:
         /* Bad permission.  */
-        return 1;
+        *fsr = 1;
+        return true;
     }
     *prot |= PAGE_EXEC;
-    return 0;
+    return false;
 }
 
 /* get_phys_addr - get the physical address for this virtual address
@@ -5806,8 +5838,8 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
  * by doing a translation table walk on MMU based systems or using the
  * MPU state on MPU based systems.
  *
- * Returns 0 if the translation was successful. Otherwise, phys_ptr, attrs,
- * prot and page_size may not be filled in, and the return value provides
+ * Returns false if the translation was successful. Otherwise, phys_ptr, attrs,
+ * prot and page_size may not be filled in, and the populated fsr value provides
  * information on why the translation aborted, in the format of a
  * DFSR/IFSR fault register, with the following caveats:
  *  * we honour the short vs long DFSR format differences.
@@ -5823,11 +5855,12 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
  * @attrs: set to the memory transaction attributes to use
  * @prot: set to the permissions for the page containing phys_ptr
  * @page_size: set to the size of the page containing phys_ptr
+ * @fsr: set to the DFSR/IFSR value on failure
  */
-static inline int get_phys_addr(CPUARMState *env, target_ulong address,
-                                int access_type, ARMMMUIdx mmu_idx,
-                                hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
-                                target_ulong *page_size)
+static inline bool get_phys_addr(CPUARMState *env, target_ulong address,
+                                 int access_type, ARMMMUIdx mmu_idx,
+                                 hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
+                                 target_ulong *page_size, uint32_t *fsr)
 {
     if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) {
         /* TODO: when we support EL2 we should here call ourselves recursively
@@ -5869,28 +5902,28 @@ static inline int get_phys_addr(CPUARMState *env, target_ulong address,
 
     if (arm_feature(env, ARM_FEATURE_MPU)) {
         *page_size = TARGET_PAGE_SIZE;
-        return get_phys_addr_mpu(env, address, access_type, mmu_idx, phys_ptr,
-                                 prot);
+        return get_phys_addr_pmsav5(env, address, access_type, mmu_idx,
+                                    phys_ptr, prot, fsr);
     }
 
     if (regime_using_lpae_format(env, mmu_idx)) {
         return get_phys_addr_lpae(env, address, access_type, mmu_idx, phys_ptr,
-                                  attrs, prot, page_size);
+                                  attrs, prot, page_size, fsr);
     } else if (regime_sctlr(env, mmu_idx) & SCTLR_XP) {
         return get_phys_addr_v6(env, address, access_type, mmu_idx, phys_ptr,
-                                attrs, prot, page_size);
+                                attrs, prot, page_size, fsr);
     } else {
         return get_phys_addr_v5(env, address, access_type, mmu_idx, phys_ptr,
-                                prot, page_size);
+                                prot, page_size, fsr);
     }
 }
 
 /* Walk the page table and (if the mapping exists) add the page
- * to the TLB. Return 0 on success, or an ARM DFSR/IFSR fault
- * register format value on failure.
+ * to the TLB. Return false on success, or true on failure. Populate
+ * fsr with ARM DFSR/IFSR fault register format value on failure.
  */
-int arm_tlb_fill(CPUState *cs, vaddr address,
-                 int access_type, int mmu_idx)
+bool arm_tlb_fill(CPUState *cs, vaddr address,
+                  int access_type, int mmu_idx, uint32_t *fsr)
 {
     ARMCPU *cpu = ARM_CPU(cs);
     CPUARMState *env = &cpu->env;
@@ -5901,8 +5934,8 @@ int arm_tlb_fill(CPUState *cs, vaddr address,
     MemTxAttrs attrs = {};
 
     ret = get_phys_addr(env, address, access_type, mmu_idx, &phys_addr,
-                        &attrs, &prot, &page_size);
-    if (ret == 0) {
+                        &attrs, &prot, &page_size, fsr);
+    if (!ret) {
         /* Map a single [sub]page.  */
         phys_addr &= TARGET_PAGE_MASK;
         address &= TARGET_PAGE_MASK;
@@ -5921,13 +5954,14 @@ hwaddr arm_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
     hwaddr phys_addr;
     target_ulong page_size;
     int prot;
-    int ret;
+    bool ret;
+    uint32_t fsr;
     MemTxAttrs attrs = {};
 
     ret = get_phys_addr(env, addr, 0, cpu_mmu_index(env), &phys_addr,
-                        &attrs, &prot, &page_size);
+                        &attrs, &prot, &page_size, &fsr);
 
-    if (ret != 0) {
+    if (ret) {
         return -1;
     }
 
diff --git a/target-arm/internals.h b/target-arm/internals.h
index 1e5071ea72..924aff9d04 100644
--- a/target-arm/internals.h
+++ b/target-arm/internals.h
@@ -388,6 +388,7 @@ void arm_handle_psci_call(ARMCPU *cpu);
 #endif
 
 /* Do a page table walk and add page to TLB if possible */
-int arm_tlb_fill(CPUState *cpu, vaddr address, int rw, int mmu_idx);
+bool arm_tlb_fill(CPUState *cpu, vaddr address, int rw, int mmu_idx,
+                  uint32_t *fsr);
 
 #endif
diff --git a/target-arm/kvm-consts.h b/target-arm/kvm-consts.h
index aea12f1bc4..943bf8980a 100644
--- a/target-arm/kvm-consts.h
+++ b/target-arm/kvm-consts.h
@@ -127,6 +127,8 @@ MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED)
 #define QEMU_KVM_ARM_TARGET_AEM_V8 0
 #define QEMU_KVM_ARM_TARGET_FOUNDATION_V8 1
 #define QEMU_KVM_ARM_TARGET_CORTEX_A57 2
+#define QEMU_KVM_ARM_TARGET_XGENE_POTENZA 3
+#define QEMU_KVM_ARM_TARGET_CORTEX_A53 4
 
 /* There's no kernel define for this: sentinel value which
  * matches no KVM target value for either 64 or 32 bit
@@ -137,6 +139,8 @@ MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED)
 MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_AEM_V8, KVM_ARM_TARGET_AEM_V8)
 MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_FOUNDATION_V8, KVM_ARM_TARGET_FOUNDATION_V8)
 MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A57, KVM_ARM_TARGET_CORTEX_A57)
+MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_XGENE_POTENZA, KVM_ARM_TARGET_XGENE_POTENZA)
+MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A53, KVM_ARM_TARGET_CORTEX_A53)
 #else
 MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A15, KVM_ARM_TARGET_CORTEX_A15)
 MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A7, KVM_ARM_TARGET_CORTEX_A7)
diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c
index 49b6babc05..d7e7d6877f 100644
--- a/target-arm/kvm32.c
+++ b/target-arm/kvm32.c
@@ -153,10 +153,14 @@ bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx)
     }
 }
 
+#define ARM_MPIDR_HWID_BITMASK 0xFFFFFF
+#define ARM_CPU_ID_MPIDR       0, 0, 0, 5
+
 int kvm_arch_init_vcpu(CPUState *cs)
 {
     int ret;
     uint64_t v;
+    uint32_t mpidr;
     struct kvm_one_reg r;
     ARMCPU *cpu = ARM_CPU(cs);
 
@@ -193,6 +197,17 @@ int kvm_arch_init_vcpu(CPUState *cs)
         return -EINVAL;
     }
 
+    /*
+     * When KVM is in use, PSCI is emulated in-kernel and not by qemu.
+     * Currently KVM has its own idea about MPIDR assignment, so we
+     * override our defaults with what we get from KVM.
+     */
+    ret = kvm_get_one_reg(cs, ARM_CP15_REG32(ARM_CPU_ID_MPIDR), &mpidr);
+    if (ret) {
+        return ret;
+    }
+    cpu->mp_affinity = mpidr & ARM_MPIDR_HWID_BITMASK;
+
     return kvm_arm_init_cpreg_list(cpu);
 }
 
diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c
index 93c1ca8b21..ac34f51498 100644
--- a/target-arm/kvm64.c
+++ b/target-arm/kvm64.c
@@ -77,9 +77,13 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
     return true;
 }
 
+#define ARM_MPIDR_HWID_BITMASK 0xFF00FFFFFFULL
+#define ARM_CPU_ID_MPIDR       3, 0, 0, 0, 5
+
 int kvm_arch_init_vcpu(CPUState *cs)
 {
     int ret;
+    uint64_t mpidr;
     ARMCPU *cpu = ARM_CPU(cs);
 
     if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE ||
@@ -107,6 +111,17 @@ int kvm_arch_init_vcpu(CPUState *cs)
         return ret;
     }
 
+    /*
+     * When KVM is in use, PSCI is emulated in-kernel and not by qemu.
+     * Currently KVM has its own idea about MPIDR assignment, so we
+     * override our defaults with what we get from KVM.
+     */
+    ret = kvm_get_one_reg(cs, ARM64_SYS_REG(ARM_CPU_ID_MPIDR), &mpidr);
+    if (ret) {
+        return ret;
+    }
+    cpu->mp_affinity = mpidr & ARM_MPIDR_HWID_BITMASK;
+
     return kvm_arm_init_cpreg_list(cpu);
 }
 
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index 7583ae7121..7fa32c4707 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -81,9 +81,10 @@ uint32_t HELPER(neon_tbl)(CPUARMState *env, uint32_t ireg, uint32_t def,
 void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
               uintptr_t retaddr)
 {
-    int ret;
+    bool ret;
+    uint32_t fsr = 0;
 
-    ret = arm_tlb_fill(cs, addr, is_write, mmu_idx);
+    ret = arm_tlb_fill(cs, addr, is_write, mmu_idx, &fsr);
     if (unlikely(ret)) {
         ARMCPU *cpu = ARM_CPU(cs);
         CPUARMState *env = &cpu->env;
@@ -96,7 +97,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
         }
 
         /* AArch64 syndrome does not have an LPAE bit */
-        syn = ret & ~(1 << 9);
+        syn = fsr & ~(1 << 9);
 
         /* For insn and data aborts we assume there is no instruction syndrome
          * information; this is always true for exceptions reported to EL1.
@@ -107,13 +108,13 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
         } else {
             syn = syn_data_abort(same_el, 0, 0, 0, is_write == 1, syn);
             if (is_write == 1 && arm_feature(env, ARM_FEATURE_V6)) {
-                ret |= (1 << 11);
+                fsr |= (1 << 11);
             }
             exc = EXCP_DATA_ABORT;
         }
 
         env->exception.vaddress = addr;
-        env->exception.fsr = ret;
+        env->exception.fsr = fsr;
         raise_exception(env, exc, syn, exception_target_el(env));
     }
 }
diff --git a/target-arm/psci.c b/target-arm/psci.c
index d8fafab2fe..20e4cb6f9c 100644
--- a/target-arm/psci.c
+++ b/target-arm/psci.c
@@ -72,6 +72,21 @@ bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
     }
 }
 
+static CPUState *get_cpu_by_id(uint64_t id)
+{
+    CPUState *cpu;
+
+    CPU_FOREACH(cpu) {
+        ARMCPU *armcpu = ARM_CPU(cpu);
+
+        if (armcpu->mp_affinity == id) {
+            return cpu;
+        }
+    }
+
+    return NULL;
+}
+
 void arm_handle_psci_call(ARMCPU *cpu)
 {
     /*
@@ -121,7 +136,7 @@ void arm_handle_psci_call(ARMCPU *cpu)
 
         switch (param[2]) {
         case 0:
-            target_cpu_state = qemu_get_cpu(mpidr & 0xff);
+            target_cpu_state = get_cpu_by_id(mpidr);
             if (!target_cpu_state) {
                 ret = QEMU_PSCI_RET_INVALID_PARAMS;
                 break;
@@ -153,7 +168,7 @@ void arm_handle_psci_call(ARMCPU *cpu)
         context_id = param[3];
 
         /* change to the cpu we are powering up */
-        target_cpu_state = qemu_get_cpu(mpidr & 0xff);
+        target_cpu_state = get_cpu_by_id(mpidr);
         if (!target_cpu_state) {
             ret = QEMU_PSCI_RET_INVALID_PARAMS;
             break;
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 39692d7a8e..ead08f4820 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -7175,7 +7175,7 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn)
                 break;
             }
 
-            gen_set_pc_im(s, s->pc);
+            gen_set_pc_im(s, s->pc - 4);
             tmpptr = tcg_const_ptr(ri);
             tcg_syn = tcg_const_i32(syndrome);
             gen_helper_access_check_cp_reg(cpu_env, tmpptr, tcg_syn);
@@ -9444,6 +9444,9 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
 
         op = (insn >> 21) & 0xf;
         if (op == 6) {
+            if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+                goto illegal_op;
+            }
             /* Halfword pack.  */
             tmp = load_reg(s, rn);
             tmp2 = load_reg(s, rm);
@@ -9508,6 +9511,27 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
             store_reg_bx(s, rd, tmp);
             break;
         case 1: /* Sign/zero extend.  */
+            op = (insn >> 20) & 7;
+            switch (op) {
+            case 0: /* SXTAH, SXTH */
+            case 1: /* UXTAH, UXTH */
+            case 4: /* SXTAB, SXTB */
+            case 5: /* UXTAB, UXTB */
+                break;
+            case 2: /* SXTAB16, SXTB16 */
+            case 3: /* UXTAB16, UXTB16 */
+                if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+                    goto illegal_op;
+                }
+                break;
+            default:
+                goto illegal_op;
+            }
+            if (rn != 15) {
+                if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+                    goto illegal_op;
+                }
+            }
             tmp = load_reg(s, rm);
             shift = (insn >> 4) & 3;
             /* ??? In many cases it's not necessary to do a
@@ -9522,7 +9546,8 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
             case 3: gen_uxtb16(tmp); break;
             case 4: gen_sxtb(tmp);   break;
             case 5: gen_uxtb(tmp);   break;
-            default: goto illegal_op;
+            default:
+                g_assert_not_reached();
             }
             if (rn != 15) {
                 tmp2 = load_reg(s, rn);
@@ -9536,6 +9561,9 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
             store_reg(s, rd, tmp);
             break;
         case 2: /* SIMD add/subtract.  */
+            if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+                goto illegal_op;
+            }
             op = (insn >> 20) & 7;
             shift = (insn >> 4) & 7;
             if ((op & 3) == 3 || (shift & 3) == 3)
@@ -9550,6 +9578,9 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
             op = ((insn >> 17) & 0x38) | ((insn >> 4) & 7);
             if (op < 4) {
                 /* Saturating add/subtract.  */
+                if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+                    goto illegal_op;
+                }
                 tmp = load_reg(s, rn);
                 tmp2 = load_reg(s, rm);
                 if (op & 1)
@@ -9560,6 +9591,31 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                     gen_helper_add_saturate(tmp, cpu_env, tmp, tmp2);
                 tcg_temp_free_i32(tmp2);
             } else {
+                switch (op) {
+                case 0x0a: /* rbit */
+                case 0x08: /* rev */
+                case 0x09: /* rev16 */
+                case 0x0b: /* revsh */
+                case 0x18: /* clz */
+                    break;
+                case 0x10: /* sel */
+                    if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+                        goto illegal_op;
+                    }
+                    break;
+                case 0x20: /* crc32/crc32c */
+                case 0x21:
+                case 0x22:
+                case 0x28:
+                case 0x29:
+                case 0x2a:
+                    if (!arm_dc_feature(s, ARM_FEATURE_CRC)) {
+                        goto illegal_op;
+                    }
+                    break;
+                default:
+                    goto illegal_op;
+                }
                 tmp = load_reg(s, rn);
                 switch (op) {
                 case 0x0a: /* rbit */
@@ -9596,10 +9652,6 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                     uint32_t sz = op & 0x3;
                     uint32_t c = op & 0x8;
 
-                    if (!arm_dc_feature(s, ARM_FEATURE_CRC)) {
-                        goto illegal_op;
-                    }
-
                     tmp2 = load_reg(s, rm);
                     if (sz == 0) {
                         tcg_gen_andi_i32(tmp2, tmp2, 0xff);
@@ -9617,12 +9669,26 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                     break;
                 }
                 default:
-                    goto illegal_op;
+                    g_assert_not_reached();
                 }
             }
             store_reg(s, rd, tmp);
             break;
         case 4: case 5: /* 32-bit multiply.  Sum of absolute differences.  */
+            switch ((insn >> 20) & 7) {
+            case 0: /* 32 x 32 -> 32 */
+            case 7: /* Unsigned sum of absolute differences.  */
+                break;
+            case 1: /* 16 x 16 -> 32 */
+            case 2: /* Dual multiply add.  */
+            case 3: /* 32 * 16 -> 32msb */
+            case 4: /* Dual multiply subtract.  */
+            case 5: case 6: /* 32 * 32 -> 32msb (SMMUL, SMMLA, SMMLS) */
+                if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+                    goto illegal_op;
+                }
+                break;
+            }
             op = (insn >> 4) & 0xf;
             tmp = load_reg(s, rn);
             tmp2 = load_reg(s, rm);
@@ -9735,6 +9801,11 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                 store_reg(s, rd, tmp);
             } else if ((op & 0xe) == 0xc) {
                 /* Dual multiply accumulate long.  */
+                if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+                    tcg_temp_free_i32(tmp);
+                    tcg_temp_free_i32(tmp2);
+                    goto illegal_op;
+                }
                 if (op & 1)
                     gen_swap_half(tmp2);
                 gen_smul_dual(tmp, tmp2);
@@ -9758,6 +9829,11 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                 } else {
                     if (op & 8) {
                         /* smlalxy */
+                        if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+                            tcg_temp_free_i32(tmp2);
+                            tcg_temp_free_i32(tmp);
+                            goto illegal_op;
+                        }
                         gen_mulxy(tmp, tmp2, op & 2, op & 1);
                         tcg_temp_free_i32(tmp2);
                         tmp64 = tcg_temp_new_i64();
@@ -9770,6 +9846,10 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                 }
                 if (op & 4) {
                     /* umaal */
+                    if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+                        tcg_temp_free_i64(tmp64);
+                        goto illegal_op;
+                    }
                     gen_addq_lo(s, tmp64, rs);
                     gen_addq_lo(s, tmp64, rd);
                 } else if (op & 0x40) {
@@ -10034,16 +10114,28 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                         tmp2 = tcg_const_i32(imm);
                         if (op & 4) {
                             /* Unsigned.  */
-                            if ((op & 1) && shift == 0)
+                            if ((op & 1) && shift == 0) {
+                                if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+                                    tcg_temp_free_i32(tmp);
+                                    tcg_temp_free_i32(tmp2);
+                                    goto illegal_op;
+                                }
                                 gen_helper_usat16(tmp, cpu_env, tmp, tmp2);
-                            else
+                            } else {
                                 gen_helper_usat(tmp, cpu_env, tmp, tmp2);
+                            }
                         } else {
                             /* Signed.  */
-                            if ((op & 1) && shift == 0)
+                            if ((op & 1) && shift == 0) {
+                                if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+                                    tcg_temp_free_i32(tmp);
+                                    tcg_temp_free_i32(tmp2);
+                                    goto illegal_op;
+                                }
                                 gen_helper_ssat16(tmp, cpu_env, tmp, tmp2);
-                            else
+                            } else {
                                 gen_helper_ssat(tmp, cpu_env, tmp, tmp2);
+                            }
                         }
                         tcg_temp_free_i32(tmp2);
                         break;
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 584e74b89a..d63eb51186 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -1100,6 +1100,7 @@ uint32_t set_cc_nz_f128(float128 v);
 
 /* misc_helper.c */
 #ifndef CONFIG_USER_ONLY
+int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3);
 void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3);
 #endif
 void program_interrupt(CPUS390XState *env, uint32_t code, int ilen);
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index f6f61b9619..b02ff8d61d 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -98,6 +98,7 @@
 #define PRIV_E3_MPCIFC                  0xd0
 #define PRIV_E3_STPCIFC                 0xd4
 
+#define DIAG_TIMEREVENT                 0x288
 #define DIAG_IPL                        0x308
 #define DIAG_KVM_HYPERCALL              0x500
 #define DIAG_KVM_BREAKPOINT             0x501
@@ -1267,6 +1268,20 @@ static int handle_hypercall(S390CPU *cpu, struct kvm_run *run)
     return ret;
 }
 
+static void kvm_handle_diag_288(S390CPU *cpu, struct kvm_run *run)
+{
+    uint64_t r1, r3;
+    int rc;
+
+    cpu_synchronize_state(CPU(cpu));
+    r1 = (run->s390_sieic.ipa & 0x00f0) >> 4;
+    r3 = run->s390_sieic.ipa & 0x000f;
+    rc = handle_diag_288(&cpu->env, r1, r3);
+    if (rc) {
+        enter_pgmcheck(cpu, PGM_SPECIFICATION);
+    }
+}
+
 static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run)
 {
     uint64_t r1, r3;
@@ -1306,6 +1321,9 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
      */
     func_code = decode_basedisp_rs(&cpu->env, ipb, NULL) & DIAG_KVM_CODE_MASK;
     switch (func_code) {
+    case DIAG_TIMEREVENT:
+        kvm_handle_diag_288(cpu, run);
+        break;
     case DIAG_IPL:
         kvm_handle_diag_308(cpu, run);
         break;
diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c
index b375ab724b..6711504221 100644
--- a/target-s390x/misc_helper.c
+++ b/target-s390x/misc_helper.c
@@ -30,6 +30,7 @@
 #include <linux/kvm.h>
 #endif
 #include "exec/cpu_ldst.h"
+#include "hw/watchdog/wdt_diag288.h"
 
 #if !defined(CONFIG_USER_ONLY)
 #include "sysemu/cpus.h"
@@ -153,6 +154,34 @@ static int load_normal_reset(S390CPU *cpu)
     return 0;
 }
 
+int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
+{
+    uint64_t func = env->regs[r1];
+    uint64_t timeout = env->regs[r1 + 1];
+    uint64_t action = env->regs[r3];
+    Object *obj;
+    DIAG288State *diag288;
+    DIAG288Class *diag288_class;
+
+    if (r1 % 2 || action != 0) {
+        return -1;
+    }
+
+    /* Timeout must be more than 15 seconds except for timer deletion */
+    if (func != WDT_DIAG288_CANCEL && timeout < 15) {
+        return -1;
+    }
+
+    obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL);
+    if (!obj) {
+        return -1;
+    }
+
+    diag288 = DIAG288(obj);
+    diag288_class = DIAG288_GET_CLASS(diag288);
+    return diag288_class->handle_timer(diag288, func, timeout);
+}
+
 #define DIAG_308_RC_OK              0x0001
 #define DIAG_308_RC_NO_CONF         0x0102
 #define DIAG_308_RC_INVALID         0x0402
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
index a9296f0833..a136f2addf 100644
--- a/tests/check-qdict.c
+++ b/tests/check-qdict.c
@@ -152,6 +152,28 @@ static void qdict_get_try_str_test(void)
     QDECREF(tests_dict);
 }
 
+static void qdict_defaults_test(void)
+{
+    QDict *dict, *copy;
+
+    dict = qdict_new();
+    copy = qdict_new();
+
+    qdict_set_default_str(dict, "foo", "abc");
+    qdict_set_default_str(dict, "foo", "def");
+    g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc");
+    qdict_set_default_str(dict, "bar", "ghi");
+
+    qdict_copy_default(copy, dict, "foo");
+    g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc");
+    qdict_set_default_str(copy, "bar", "xyz");
+    qdict_copy_default(copy, dict, "bar");
+    g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz");
+
+    QDECREF(copy);
+    QDECREF(dict);
+}
+
 static void qdict_haskey_not_test(void)
 {
     QDict *tests_dict = qdict_new();
@@ -444,6 +466,49 @@ static void qdict_array_split_test(void)
     QDECREF(test_dict);
 }
 
+static void qdict_array_entries_test(void)
+{
+    QDict *dict = qdict_new();
+
+    g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0);
+
+    qdict_put(dict, "bar", qint_from_int(0));
+    qdict_put(dict, "baz.0", qint_from_int(0));
+    g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0);
+
+    qdict_put(dict, "foo.1", qint_from_int(0));
+    g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL);
+    qdict_put(dict, "foo.0", qint_from_int(0));
+    g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2);
+    qdict_put(dict, "foo.bar", qint_from_int(0));
+    g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL);
+    qdict_del(dict, "foo.bar");
+
+    qdict_put(dict, "foo.2.a", qint_from_int(0));
+    qdict_put(dict, "foo.2.b", qint_from_int(0));
+    qdict_put(dict, "foo.2.c", qint_from_int(0));
+    g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3);
+    g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
+
+    QDECREF(dict);
+
+    dict = qdict_new();
+    qdict_put(dict, "1", qint_from_int(0));
+    g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
+    qdict_put(dict, "0", qint_from_int(0));
+    g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2);
+    qdict_put(dict, "bar", qint_from_int(0));
+    g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
+    qdict_del(dict, "bar");
+
+    qdict_put(dict, "2.a", qint_from_int(0));
+    qdict_put(dict, "2.b", qint_from_int(0));
+    qdict_put(dict, "2.c", qint_from_int(0));
+    g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3);
+
+    QDECREF(dict);
+}
+
 static void qdict_join_test(void)
 {
     QDict *dict1, *dict2;
@@ -663,6 +728,7 @@ int main(int argc, char **argv)
     g_test_add_func("/public/get_try_int", qdict_get_try_int_test);
     g_test_add_func("/public/get_str", qdict_get_str_test);
     g_test_add_func("/public/get_try_str", qdict_get_try_str_test);
+    g_test_add_func("/public/defaults", qdict_defaults_test);
     g_test_add_func("/public/haskey_not", qdict_haskey_not_test);
     g_test_add_func("/public/haskey", qdict_haskey_test);
     g_test_add_func("/public/del", qdict_del_test);
@@ -670,6 +736,7 @@ int main(int argc, char **argv)
     g_test_add_func("/public/iterapi", qdict_iterapi_test);
     g_test_add_func("/public/flatten", qdict_flatten_test);
     g_test_add_func("/public/array_split", qdict_array_split_test);
+    g_test_add_func("/public/array_entries", qdict_array_entries_test);
     g_test_add_func("/public/join", qdict_join_test);
 
     g_test_add_func("/errors/put_exists", qdict_put_exists_test);
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
index 0360f37e5a..4a8055b673 100755
--- a/tests/qemu-iotests/051
+++ b/tests/qemu-iotests/051
@@ -194,7 +194,6 @@ echo === Specifying the protocol layer ===
 echo
 
 run_qemu -drive file="$TEST_IMG",file.driver=file
-run_qemu -drive file="$TEST_IMG",file.driver=qcow2
 
 echo
 echo === Leaving out required options ===
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index 2890eac084..652dd63bf8 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -253,9 +253,6 @@ Testing: -drive file=TEST_DIR/t.qcow2,file.driver=file
 QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) qququiquit
 
-Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename'
-
 
 === Leaving out required options ===
 
diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
index b9096a55d4..c0e9e2b0b5 100755
--- a/tests/qemu-iotests/093
+++ b/tests/qemu-iotests/093
@@ -3,6 +3,7 @@
 # Tests for IO throttling
 #
 # Copyright (C) 2015 Red Hat, Inc.
+# Copyright (C) 2015 Igalia, S.L.
 #
 # 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
@@ -22,6 +23,7 @@ import iotests
 
 class ThrottleTestCase(iotests.QMPTestCase):
     test_img = "null-aio://"
+    max_drives = 3
 
     def blockstats(self, device):
         result = self.vm.qmp("query-blockstats")
@@ -32,26 +34,31 @@ class ThrottleTestCase(iotests.QMPTestCase):
         raise Exception("Device not found for blockstats: %s" % device)
 
     def setUp(self):
-        self.vm = iotests.VM().add_drive(self.test_img)
+        self.vm = iotests.VM()
+        for i in range(0, self.max_drives):
+            self.vm.add_drive(self.test_img)
         self.vm.launch()
 
     def tearDown(self):
         self.vm.shutdown()
 
-    def do_test_throttle(self, seconds, params):
+    def do_test_throttle(self, ndrives, seconds, params):
         def check_limit(limit, num):
             # IO throttling algorithm is discrete, allow 10% error so the test
             # is more robust
             return limit == 0 or \
-                   (num < seconds * limit * 1.1
-                   and num > seconds * limit * 0.9)
+                   (num < seconds * limit * 1.1 / ndrives
+                   and num > seconds * limit * 0.9 / ndrives)
 
         nsec_per_sec = 1000000000
 
-        params['device'] = 'drive0'
+        params['group'] = 'test'
 
-        result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
-        self.assert_qmp(result, 'return', {})
+        # Set the I/O throttling parameters to all drives
+        for i in range(0, ndrives):
+            params['device'] = 'drive%d' % i
+            result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
+            self.assert_qmp(result, 'return', {})
 
         # Set vm clock to a known value
         ns = seconds * nsec_per_sec
@@ -66,32 +73,60 @@ class ThrottleTestCase(iotests.QMPTestCase):
                     params['iops'] / 2,
                     params['iops_rd'])
         rd_nr *= seconds * 2
+        rd_nr /= ndrives
         wr_nr = max(params['bps'] / rq_size / 2,
                     params['bps_wr'] / rq_size,
                     params['iops'] / 2,
                     params['iops_wr'])
         wr_nr *= seconds * 2
+        wr_nr /= ndrives
+
+        # Send I/O requests to all drives
         for i in range(rd_nr):
-            self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % (i * rq_size, rq_size))
-        for i in range(wr_nr):
-            self.vm.hmp_qemu_io("drive0", "aio_write %d %d" % (i * rq_size, rq_size))
+            for drive in range(0, ndrives):
+                self.vm.hmp_qemu_io("drive%d" % drive, "aio_read %d %d" %
+                                    (i * rq_size, rq_size))
 
-        start_rd_bytes, start_rd_iops, start_wr_bytes, start_wr_iops = self.blockstats('drive0')
+        for i in range(wr_nr):
+            for drive in range(0, ndrives):
+                self.vm.hmp_qemu_io("drive%d" % drive, "aio_write %d %d" %
+                                    (i * rq_size, rq_size))
+
+        # We'll store the I/O stats for each drive in these arrays
+        start_rd_bytes = [0] * ndrives
+        start_rd_iops  = [0] * ndrives
+        start_wr_bytes = [0] * ndrives
+        start_wr_iops  = [0] * ndrives
+        end_rd_bytes   = [0] * ndrives
+        end_rd_iops    = [0] * ndrives
+        end_wr_bytes   = [0] * ndrives
+        end_wr_iops    = [0] * ndrives
+
+        # Read the stats before advancing the clock
+        for i in range(0, ndrives):
+            start_rd_bytes[i], start_rd_iops[i], start_wr_bytes[i], \
+                start_wr_iops[i] = self.blockstats('drive%d' % i)
 
         self.vm.qtest("clock_step %d" % ns)
-        end_rd_bytes, end_rd_iops, end_wr_bytes, end_wr_iops = self.blockstats('drive0')
-
-        rd_bytes = end_rd_bytes - start_rd_bytes
-        rd_iops = end_rd_iops - start_rd_iops
-        wr_bytes = end_wr_bytes - start_wr_bytes
-        wr_iops = end_wr_iops - start_wr_iops
 
-        self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes))
-        self.assertTrue(check_limit(params['bps_rd'], rd_bytes))
-        self.assertTrue(check_limit(params['bps_wr'], wr_bytes))
-        self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops))
-        self.assertTrue(check_limit(params['iops_rd'], rd_iops))
-        self.assertTrue(check_limit(params['iops_wr'], wr_iops))
+        # Read the stats after advancing the clock
+        for i in range(0, ndrives):
+            end_rd_bytes[i], end_rd_iops[i], end_wr_bytes[i], \
+                end_wr_iops[i] = self.blockstats('drive%d' % i)
+
+        # Check that the I/O is within the limits and evenly distributed
+        for i in range(0, ndrives):
+            rd_bytes = end_rd_bytes[i] - start_rd_bytes[i]
+            rd_iops = end_rd_iops[i] - start_rd_iops[i]
+            wr_bytes = end_wr_bytes[i] - start_wr_bytes[i]
+            wr_iops = end_wr_iops[i] - start_wr_iops[i]
+
+            self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes))
+            self.assertTrue(check_limit(params['bps_rd'], rd_bytes))
+            self.assertTrue(check_limit(params['bps_wr'], wr_bytes))
+            self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops))
+            self.assertTrue(check_limit(params['iops_rd'], rd_iops))
+            self.assertTrue(check_limit(params['iops_wr'], wr_iops))
 
     def test_all(self):
         params = {"bps": 4096,
@@ -101,11 +136,13 @@ class ThrottleTestCase(iotests.QMPTestCase):
                   "iops_rd": 10,
                   "iops_wr": 10,
                  }
-        # Pick each out of all possible params and test
-        for tk in params:
-            limits = dict([(k, 0) for k in params])
-            limits[tk] = params[tk]
-            self.do_test_throttle(5, limits)
+        # Repeat the test with different numbers of drives
+        for ndrives in range(1, self.max_drives + 1):
+            # Pick each out of all possible params and test
+            for tk in params:
+                limits = dict([(k, 0) for k in params])
+                limits[tk] = params[tk] * ndrives
+                self.do_test_throttle(ndrives, 5, limits)
 
 class ThrottleTestCoroutine(ThrottleTestCase):
     test_img = "null-co://"
diff --git a/tests/qemu-iotests/103 b/tests/qemu-iotests/103
index ccab551f63..fa9a3c1fc9 100755
--- a/tests/qemu-iotests/103
+++ b/tests/qemu-iotests/103
@@ -93,6 +93,16 @@ $QEMU_IO -c "open -o l2-cache-size=1M,refcount-cache-size=0.25M $TEST_IMG" \
          -c 'read -P 42 0 64k' \
     | _filter_qemu_io
 
+echo
+echo '=== Testing minimal L2 cache and COW ==='
+echo
+
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
+# This requires a COW operation, which accesses two L2 tables simultaneously
+# (COW source and destination), so there must be enough space in the cache to
+# place both tables there (and qemu should not crash)
+$QEMU_IO -c "open -o cache-size=0 $TEST_IMG" -c 'write 0 64k' | _filter_qemu_io
+
 # success, all done
 echo '*** done'
 rm -f $seq.full
diff --git a/tests/qemu-iotests/103.out b/tests/qemu-iotests/103.out
index ee705b05f0..d05f49fdba 100644
--- a/tests/qemu-iotests/103.out
+++ b/tests/qemu-iotests/103.out
@@ -26,4 +26,9 @@ read 65536/65536 bytes at offset 0
 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 read 65536/65536 bytes at offset 0
 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Testing minimal L2 cache and COW ===
+
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 *** done
diff --git a/tests/qemu-iotests/119 b/tests/qemu-iotests/119
new file mode 100755
index 0000000000..9a11f1b921
--- /dev/null
+++ b/tests/qemu-iotests/119
@@ -0,0 +1,60 @@
+#!/bin/bash
+#
+# NBD test case for overriding BDRV_O_PROTOCOL by explicitly specifying
+# a driver
+#
+# Copyright (C) 2015 Red Hat, Inc.
+#
+# 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/>.
+#
+
+# creator
+owner=mreitz@redhat.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+tmp=/tmp/$$
+status=1	# failure is the default!
+
+_cleanup()
+{
+	_cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt raw
+_supported_proto nbd
+_supported_os Linux
+
+_make_test_img 64M
+# This should not crash
+echo "{'execute': 'qmp_capabilities'}
+      {'execute': 'human-monitor-command',
+       'arguments': {'command-line': 'qemu-io drv \"read -P 0 0 64k\"'}}
+      {'execute': 'quit'}" \
+    | $QEMU -drive id=drv,if=none,file="$TEST_IMG",driver=nbd \
+            -qmp stdio -nodefaults \
+    | _filter_qmp | _filter_qemu_io
+
+# success, all done
+echo
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/119.out b/tests/qemu-iotests/119.out
new file mode 100644
index 0000000000..58e7114e8b
--- /dev/null
+++ b/tests/qemu-iotests/119.out
@@ -0,0 +1,11 @@
+QA output created by 119
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+QMP_VERSION
+{"return": {}}
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+{"return": ""}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN"}
+
+*** done
diff --git a/tests/qemu-iotests/120 b/tests/qemu-iotests/120
new file mode 100755
index 0000000000..9f13078764
--- /dev/null
+++ b/tests/qemu-iotests/120
@@ -0,0 +1,65 @@
+#!/bin/bash
+#
+# Non-NBD test cases for overriding BDRV_O_PROTOCOL by explicitly
+# specifying a driver
+#
+# Copyright (C) 2015 Red Hat, Inc.
+#
+# 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/>.
+#
+
+# creator
+owner=mreitz@redhat.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+tmp=/tmp/$$
+status=1	# failure is the default!
+
+_cleanup()
+{
+	_cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt generic
+_supported_proto file
+_supported_os Linux
+
+_make_test_img 64M
+
+echo "{'execute': 'qmp_capabilities'}
+      {'execute': 'human-monitor-command',
+       'arguments': {'command-line': 'qemu-io drv \"write -P 42 0 64k\"'}}
+      {'execute': 'quit'}" \
+    | $QEMU -qmp stdio -nodefaults \
+            -drive id=drv,if=none,file="$TEST_IMG",driver=raw,file.driver=$IMGFMT \
+    | _filter_qmp | _filter_qemu_io
+$QEMU_IO -c 'read -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+$QEMU_IO_PROG -c 'read -P 42 0 64k' \
+    "json:{'driver': 'raw', 'file': {'driver': '$IMGFMT', 'file': {'filename': '$TEST_IMG'}}}" \
+    | _filter_qemu_io
+
+# success, all done
+echo
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/120.out b/tests/qemu-iotests/120.out
new file mode 100644
index 0000000000..9131b1bce9
--- /dev/null
+++ b/tests/qemu-iotests/120.out
@@ -0,0 +1,15 @@
+QA output created by 120
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+QMP_VERSION
+{"return": {}}
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+{"return": ""}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN"}
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+*** done
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
index 3ee78cd1f1..8abce2f869 100644
--- a/tests/qemu-iotests/124
+++ b/tests/qemu-iotests/124
@@ -125,7 +125,7 @@ class TestIncrementalBackup(iotests.QMPTestCase):
 
         event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
                                    match={'data': {'device': kwargs['device']}})
-        self.assertIsNotNone(event)
+        self.assertNotEqual(event, None)
 
         try:
             failure = self.dictpath(event, 'data/error')
diff --git a/tests/qemu-iotests/128 b/tests/qemu-iotests/128
index 249a865581..e2a0f2f890 100755
--- a/tests/qemu-iotests/128
+++ b/tests/qemu-iotests/128
@@ -29,6 +29,7 @@ tmp=/tmp/$$
 status=1	# failure is the default!
 
 devname="eiodev$$"
+sudo=""
 
 _setup_eiodev()
 {
@@ -37,6 +38,7 @@ _setup_eiodev()
 		echo "0 $((1024 * 1024 * 1024 / 512)) error" | \
 			$cmd dmsetup create "$devname" 2>/dev/null
 		if [ "$?" -eq 0 ]; then
+			sudo="$cmd"
 			return
 		fi
 	done
@@ -74,7 +76,7 @@ TEST_IMG="/dev/mapper/$devname"
 echo
 echo "== reading from error device =="
 # Opening image should succeed but the read operation should fail
-$QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io
+$sudo $QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io
 
 # success, all done
 echo "*** done"
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 0b817ca32d..4597fc11c0 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -121,6 +121,8 @@
 114 rw auto quick
 115 rw auto
 116 rw auto quick
+119 rw auto quick
+120 rw auto quick
 121 rw auto
 122 rw auto
 123 rw auto quick
diff --git a/tests/test-aio.c b/tests/test-aio.c
index 4b0cb45d31..a7cb5c9915 100644
--- a/tests/test-aio.c
+++ b/tests/test-aio.c
@@ -107,7 +107,6 @@ static void test_notify(void)
 
 typedef struct {
     QemuMutex start_lock;
-    EventNotifier notifier;
     bool thread_acquired;
 } AcquireTestData;
 
@@ -119,8 +118,6 @@ static void *test_acquire_thread(void *opaque)
     qemu_mutex_lock(&data->start_lock);
     qemu_mutex_unlock(&data->start_lock);
 
-    g_usleep(500000);
-    event_notifier_set(&data->notifier);
     aio_context_acquire(ctx);
     aio_context_release(ctx);
 
@@ -129,19 +126,20 @@ static void *test_acquire_thread(void *opaque)
     return NULL;
 }
 
-static void dummy_notifier_read(EventNotifier *n)
+static void dummy_notifier_read(EventNotifier *unused)
 {
-    event_notifier_test_and_clear(n);
+    g_assert(false); /* should never be invoked */
 }
 
 static void test_acquire(void)
 {
     QemuThread thread;
+    EventNotifier notifier;
     AcquireTestData data;
 
     /* Dummy event notifier ensures aio_poll() will block */
-    event_notifier_init(&data.notifier, false);
-    aio_set_event_notifier(ctx, &data.notifier, dummy_notifier_read);
+    event_notifier_init(&notifier, false);
+    aio_set_event_notifier(ctx, &notifier, dummy_notifier_read);
     g_assert(!aio_poll(ctx, false)); /* consume aio_notify() */
 
     qemu_mutex_init(&data.start_lock);
@@ -155,13 +153,12 @@ static void test_acquire(void)
     /* Block in aio_poll(), let other thread kick us and acquire context */
     aio_context_acquire(ctx);
     qemu_mutex_unlock(&data.start_lock); /* let the thread run */
-    g_assert(aio_poll(ctx, true));
-    g_assert(!data.thread_acquired);
+    g_assert(!aio_poll(ctx, true));
     aio_context_release(ctx);
 
     qemu_thread_join(&thread);
-    aio_set_event_notifier(ctx, &data.notifier, NULL);
-    event_notifier_cleanup(&data.notifier);
+    aio_set_event_notifier(ctx, &notifier, NULL);
+    event_notifier_cleanup(&notifier);
 
     g_assert(data.thread_acquired);
 }
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index d8ba415e43..016844546a 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -1,10 +1,12 @@
 /*
  * Throttle infrastructure tests
  *
- * Copyright Nodalink, SARL. 2013
+ * Copyright Nodalink, EURL. 2013-2014
+ * Copyright Igalia, S.L. 2015
  *
  * Authors:
- *  Benoît Canet     <benoit.canet@irqsave.net>
+ *  Benoît Canet     <benoit.canet@nodalink.com>
+ *  Alberto Garcia   <berto@igalia.com>
  *
  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
  * See the COPYING.LIB file in the top-level directory.
@@ -15,11 +17,13 @@
 #include "block/aio.h"
 #include "qemu/throttle.h"
 #include "qemu/error-report.h"
+#include "block/throttle-groups.h"
 
 static AioContext     *ctx;
 static LeakyBucket    bkt;
 static ThrottleConfig cfg;
 static ThrottleState  ts;
+static ThrottleTimers tt;
 
 /* useful function */
 static bool double_cmp(double x, double y)
@@ -103,17 +107,19 @@ static void test_init(void)
 {
     int i;
 
-    /* fill the structure with crap */
+    /* fill the structures with crap */
     memset(&ts, 1, sizeof(ts));
+    memset(&tt, 1, sizeof(tt));
 
-    /* init the structure */
-    throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
-                  read_timer_cb, write_timer_cb, &ts);
+    /* init structures */
+    throttle_init(&ts);
+    throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+                         read_timer_cb, write_timer_cb, &ts);
 
     /* check initialized fields */
-    g_assert(ts.clock_type == QEMU_CLOCK_VIRTUAL);
-    g_assert(ts.timers[0]);
-    g_assert(ts.timers[1]);
+    g_assert(tt.clock_type == QEMU_CLOCK_VIRTUAL);
+    g_assert(tt.timers[0]);
+    g_assert(tt.timers[1]);
 
     /* check other fields where cleared */
     g_assert(!ts.previous_leak);
@@ -124,17 +130,18 @@ static void test_init(void)
         g_assert(!ts.cfg.buckets[i].level);
     }
 
-    throttle_destroy(&ts);
+    throttle_timers_destroy(&tt);
 }
 
 static void test_destroy(void)
 {
     int i;
-    throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
-                  read_timer_cb, write_timer_cb, &ts);
-    throttle_destroy(&ts);
+    throttle_init(&ts);
+    throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+                         read_timer_cb, write_timer_cb, &ts);
+    throttle_timers_destroy(&tt);
     for (i = 0; i < 2; i++) {
-        g_assert(!ts.timers[i]);
+        g_assert(!tt.timers[i]);
     }
 }
 
@@ -170,11 +177,12 @@ static void test_config_functions(void)
 
     orig_cfg.op_size = 1;
 
-    throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
-                  read_timer_cb, write_timer_cb, &ts);
+    throttle_init(&ts);
+    throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+                         read_timer_cb, write_timer_cb, &ts);
     /* structure reset by throttle_init previous_leak should be null */
     g_assert(!ts.previous_leak);
-    throttle_config(&ts, &orig_cfg);
+    throttle_config(&ts, &tt, &orig_cfg);
 
     /* has previous leak been initialized by throttle_config ? */
     g_assert(ts.previous_leak);
@@ -182,7 +190,7 @@ static void test_config_functions(void)
     /* get back the fixed configuration */
     throttle_get_config(&ts, &final_cfg);
 
-    throttle_destroy(&ts);
+    throttle_timers_destroy(&tt);
 
     g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153);
     g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg  == 56);
@@ -323,43 +331,47 @@ static void test_is_valid(void)
 
 static void test_have_timer(void)
 {
-    /* zero the structure */
+    /* zero structures */
     memset(&ts, 0, sizeof(ts));
+    memset(&tt, 0, sizeof(tt));
 
     /* no timer set should return false */
-    g_assert(!throttle_have_timer(&ts));
+    g_assert(!throttle_timers_are_initialized(&tt));
 
-    /* init the structure */
-    throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
-                  read_timer_cb, write_timer_cb, &ts);
+    /* init structures */
+    throttle_init(&ts);
+    throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+                         read_timer_cb, write_timer_cb, &ts);
 
     /* timer set by init should return true */
-    g_assert(throttle_have_timer(&ts));
+    g_assert(throttle_timers_are_initialized(&tt));
 
-    throttle_destroy(&ts);
+    throttle_timers_destroy(&tt);
 }
 
 static void test_detach_attach(void)
 {
-    /* zero the structure */
+    /* zero structures */
     memset(&ts, 0, sizeof(ts));
+    memset(&tt, 0, sizeof(tt));
 
     /* init the structure */
-    throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
-                  read_timer_cb, write_timer_cb, &ts);
+    throttle_init(&ts);
+    throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+                         read_timer_cb, write_timer_cb, &ts);
 
     /* timer set by init should return true */
-    g_assert(throttle_have_timer(&ts));
+    g_assert(throttle_timers_are_initialized(&tt));
 
     /* timer should no longer exist after detaching */
-    throttle_detach_aio_context(&ts);
-    g_assert(!throttle_have_timer(&ts));
+    throttle_timers_detach_aio_context(&tt);
+    g_assert(!throttle_timers_are_initialized(&tt));
 
     /* timer should exist again after attaching */
-    throttle_attach_aio_context(&ts, ctx);
-    g_assert(throttle_have_timer(&ts));
+    throttle_timers_attach_aio_context(&tt, ctx);
+    g_assert(throttle_timers_are_initialized(&tt));
 
-    throttle_destroy(&ts);
+    throttle_timers_destroy(&tt);
 }
 
 static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
@@ -387,9 +399,10 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
 
     cfg.op_size = op_size;
 
-    throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
-                  read_timer_cb, write_timer_cb, &ts);
-    throttle_config(&ts, &cfg);
+    throttle_init(&ts);
+    throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+                         read_timer_cb, write_timer_cb, &ts);
+    throttle_config(&ts, &tt, &cfg);
 
     /* account a read */
     throttle_account(&ts, false, size);
@@ -414,7 +427,7 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
         return false;
     }
 
-    throttle_destroy(&ts);
+    throttle_timers_destroy(&tt);
 
     return true;
 }
@@ -490,23 +503,80 @@ static void test_accounting(void)
                                 (64.0 / 13)));
 }
 
+static void test_groups(void)
+{
+    ThrottleConfig cfg1, cfg2;
+    BlockDriverState *bdrv1, *bdrv2, *bdrv3;
+
+    bdrv1 = bdrv_new();
+    bdrv2 = bdrv_new();
+    bdrv3 = bdrv_new();
+
+    g_assert(bdrv1->throttle_state == NULL);
+    g_assert(bdrv2->throttle_state == NULL);
+    g_assert(bdrv3->throttle_state == NULL);
+
+    throttle_group_register_bs(bdrv1, "bar");
+    throttle_group_register_bs(bdrv2, "foo");
+    throttle_group_register_bs(bdrv3, "bar");
+
+    g_assert(bdrv1->throttle_state != NULL);
+    g_assert(bdrv2->throttle_state != NULL);
+    g_assert(bdrv3->throttle_state != NULL);
+
+    g_assert(!strcmp(throttle_group_get_name(bdrv1), "bar"));
+    g_assert(!strcmp(throttle_group_get_name(bdrv2), "foo"));
+    g_assert(bdrv1->throttle_state == bdrv3->throttle_state);
+
+    /* Setting the config of a group member affects the whole group */
+    memset(&cfg1, 0, sizeof(cfg1));
+    cfg1.buckets[THROTTLE_BPS_READ].avg  = 500000;
+    cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000;
+    cfg1.buckets[THROTTLE_OPS_READ].avg  = 20000;
+    cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000;
+    throttle_group_config(bdrv1, &cfg1);
+
+    throttle_group_get_config(bdrv1, &cfg1);
+    throttle_group_get_config(bdrv3, &cfg2);
+    g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
+
+    cfg2.buckets[THROTTLE_BPS_READ].avg  = 4547;
+    cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349;
+    cfg2.buckets[THROTTLE_OPS_READ].avg  = 123;
+    cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86;
+    throttle_group_config(bdrv3, &cfg1);
+
+    throttle_group_get_config(bdrv1, &cfg1);
+    throttle_group_get_config(bdrv3, &cfg2);
+    g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
+
+    throttle_group_unregister_bs(bdrv1);
+    throttle_group_unregister_bs(bdrv2);
+    throttle_group_unregister_bs(bdrv3);
+
+    g_assert(bdrv1->throttle_state == NULL);
+    g_assert(bdrv2->throttle_state == NULL);
+    g_assert(bdrv3->throttle_state == NULL);
+}
+
 int main(int argc, char **argv)
 {
-    GSource *src;
     Error *local_error = NULL;
 
-    init_clocks();
+    qemu_init_main_loop(&local_error);
+    ctx = qemu_get_aio_context();
 
-    ctx = aio_context_new(&local_error);
     if (!ctx) {
         error_report("Failed to create AIO Context: '%s'",
-                     error_get_pretty(local_error));
-        error_free(local_error);
+                     local_error ? error_get_pretty(local_error) :
+                     "Failed to initialize the QEMU main loop");
+        if (local_error) {
+            error_free(local_error);
+        }
         exit(1);
     }
-    src = aio_get_g_source(ctx);
-    g_source_attach(src, NULL);
-    g_source_unref(src);
+
+    bdrv_init();
 
     do {} while (g_main_context_iteration(NULL, false));
 
@@ -523,6 +593,7 @@ int main(int argc, char **argv)
     g_test_add_func("/throttle/config/is_valid",    test_is_valid);
     g_test_add_func("/throttle/config_functions",   test_config_functions);
     g_test_add_func("/throttle/accounting",         test_accounting);
+    g_test_add_func("/throttle/groups",             test_groups);
     return g_test_run();
 }
 
diff --git a/trace-events b/trace-events
index 6060d36773..52b7efa9a4 100644
--- a/trace-events
+++ b/trace-events
@@ -1632,3 +1632,19 @@ cpu_unhalt(int cpu_index) "unhalting cpu %d"
 
 # hw/arm/virt-acpi-build.c
 virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out."
+
+# audio/alsaaudio.c
+alsa_revents(int revents) "revents = %d"
+alsa_pollout(int i, int fd) "i = %d fd = %d"
+alsa_set_handler(int events, int index, int fd, int err) "events=%#x index=%d fd=%d err=%d"
+alsa_wrote_zero(int len) "Failed to write %d frames (wrote zero)"
+alsa_read_zero(long len) "Failed to read %ld frames (read zero)"
+alsa_xrun_out(void) "Recovering from playback xrun"
+alsa_xrun_in(void) "Recovering from capture xrun"
+alsa_resume_out(void) "Resuming suspended output stream"
+alsa_resume_in(void) "Resuming suspended input stream"
+alsa_no_frames(int state) "No frames available and ALSA state is %d"
+
+# audio/ossaudio.c
+oss_version(int version) "OSS version = %#x"
+oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
diff --git a/util/throttle.c b/util/throttle.c
index f976ac7de5..706c13111e 100644
--- a/util/throttle.c
+++ b/util/throttle.c
@@ -1,10 +1,12 @@
 /*
  * QEMU throttling infrastructure
  *
- * Copyright (C) Nodalink, SARL. 2013
+ * Copyright (C) Nodalink, EURL. 2013-2014
+ * Copyright (C) Igalia, S.L. 2015
  *
- * Author:
- *   Benoît Canet <benoit.canet@irqsave.net>
+ * Authors:
+ *   Benoît Canet <benoit.canet@nodalink.com>
+ *   Alberto Garcia <berto@igalia.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -159,29 +161,36 @@ bool throttle_compute_timer(ThrottleState *ts,
 }
 
 /* Add timers to event loop */
-void throttle_attach_aio_context(ThrottleState *ts, AioContext *new_context)
+void throttle_timers_attach_aio_context(ThrottleTimers *tt,
+                                        AioContext *new_context)
 {
-    ts->timers[0] = aio_timer_new(new_context, ts->clock_type, SCALE_NS,
-                                  ts->read_timer_cb, ts->timer_opaque);
-    ts->timers[1] = aio_timer_new(new_context, ts->clock_type, SCALE_NS,
-                                  ts->write_timer_cb, ts->timer_opaque);
+    tt->timers[0] = aio_timer_new(new_context, tt->clock_type, SCALE_NS,
+                                  tt->read_timer_cb, tt->timer_opaque);
+    tt->timers[1] = aio_timer_new(new_context, tt->clock_type, SCALE_NS,
+                                  tt->write_timer_cb, tt->timer_opaque);
 }
 
 /* To be called first on the ThrottleState */
-void throttle_init(ThrottleState *ts,
-                   AioContext *aio_context,
-                   QEMUClockType clock_type,
-                   QEMUTimerCB *read_timer_cb,
-                   QEMUTimerCB *write_timer_cb,
-                   void *timer_opaque)
+void throttle_init(ThrottleState *ts)
 {
     memset(ts, 0, sizeof(ThrottleState));
+}
+
+/* To be called first on the ThrottleTimers */
+void throttle_timers_init(ThrottleTimers *tt,
+                          AioContext *aio_context,
+                          QEMUClockType clock_type,
+                          QEMUTimerCB *read_timer_cb,
+                          QEMUTimerCB *write_timer_cb,
+                          void *timer_opaque)
+{
+    memset(tt, 0, sizeof(ThrottleTimers));
 
-    ts->clock_type = clock_type;
-    ts->read_timer_cb = read_timer_cb;
-    ts->write_timer_cb = write_timer_cb;
-    ts->timer_opaque = timer_opaque;
-    throttle_attach_aio_context(ts, aio_context);
+    tt->clock_type = clock_type;
+    tt->read_timer_cb = read_timer_cb;
+    tt->write_timer_cb = write_timer_cb;
+    tt->timer_opaque = timer_opaque;
+    throttle_timers_attach_aio_context(tt, aio_context);
 }
 
 /* destroy a timer */
@@ -195,25 +204,25 @@ static void throttle_timer_destroy(QEMUTimer **timer)
 }
 
 /* Remove timers from event loop */
-void throttle_detach_aio_context(ThrottleState *ts)
+void throttle_timers_detach_aio_context(ThrottleTimers *tt)
 {
     int i;
 
     for (i = 0; i < 2; i++) {
-        throttle_timer_destroy(&ts->timers[i]);
+        throttle_timer_destroy(&tt->timers[i]);
     }
 }
 
-/* To be called last on the ThrottleState */
-void throttle_destroy(ThrottleState *ts)
+/* To be called last on the ThrottleTimers */
+void throttle_timers_destroy(ThrottleTimers *tt)
 {
-    throttle_detach_aio_context(ts);
+    throttle_timers_detach_aio_context(tt);
 }
 
 /* is any throttling timer configured */
-bool throttle_have_timer(ThrottleState *ts)
+bool throttle_timers_are_initialized(ThrottleTimers *tt)
 {
-    if (ts->timers[0]) {
+    if (tt->timers[0]) {
         return true;
     }
 
@@ -324,9 +333,12 @@ static void throttle_cancel_timer(QEMUTimer *timer)
 /* Used to configure the throttle
  *
  * @ts: the throttle state we are working on
+ * @tt: the throttle timers we use in this aio context
  * @cfg: the config to set
  */
-void throttle_config(ThrottleState *ts, ThrottleConfig *cfg)
+void throttle_config(ThrottleState *ts,
+                     ThrottleTimers *tt,
+                     ThrottleConfig *cfg)
 {
     int i;
 
@@ -336,10 +348,10 @@ void throttle_config(ThrottleState *ts, ThrottleConfig *cfg)
         throttle_fix_bucket(&ts->cfg.buckets[i]);
     }
 
-    ts->previous_leak = qemu_clock_get_ns(ts->clock_type);
+    ts->previous_leak = qemu_clock_get_ns(tt->clock_type);
 
     for (i = 0; i < 2; i++) {
-        throttle_cancel_timer(ts->timers[i]);
+        throttle_cancel_timer(tt->timers[i]);
     }
 }
 
@@ -358,12 +370,15 @@ void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg)
  *
  * NOTE: this function is not unit tested due to it's usage of timer_mod
  *
+ * @tt:       the timers structure
  * @is_write: the type of operation (read/write)
  * @ret:      true if the timer has been scheduled else false
  */
-bool throttle_schedule_timer(ThrottleState *ts, bool is_write)
+bool throttle_schedule_timer(ThrottleState *ts,
+                             ThrottleTimers *tt,
+                             bool is_write)
 {
-    int64_t now = qemu_clock_get_ns(ts->clock_type);
+    int64_t now = qemu_clock_get_ns(tt->clock_type);
     int64_t next_timestamp;
     bool must_wait;
 
@@ -378,12 +393,12 @@ bool throttle_schedule_timer(ThrottleState *ts, bool is_write)
     }
 
     /* request throttled and timer pending -> do nothing */
-    if (timer_pending(ts->timers[is_write])) {
+    if (timer_pending(tt->timers[is_write])) {
         return true;
     }
 
     /* request throttled and timer not pending -> arm timer */
-    timer_mod(ts->timers[is_write], next_timestamp);
+    timer_mod(tt->timers[is_write], next_timestamp);
     return true;
 }