aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS2
-rw-r--r--Makefile.objs1
-rw-r--r--audio/Makefile.objs2
-rw-r--r--audio/alsaaudio.c370
-rw-r--r--audio/audio.c859
-rw-r--r--audio/audio.h30
-rw-r--r--audio/audio_int.h37
-rw-r--r--audio/audio_legacy.c544
-rw-r--r--audio/audio_template.h42
-rw-r--r--audio/audio_win_int.c18
-rw-r--r--audio/coreaudio.c51
-rw-r--r--audio/dsound_template.h6
-rw-r--r--audio/dsoundaudio.c61
-rw-r--r--audio/noaudio.c3
-rw-r--r--audio/ossaudio.c191
-rw-r--r--audio/paaudio.c111
-rw-r--r--audio/sdlaudio.c50
-rw-r--r--audio/spiceaudio.c11
-rw-r--r--audio/wavaudio.c75
-rw-r--r--audio/wavcapture.c2
-rw-r--r--backends/hostmem-file.c23
-rw-r--r--backends/hostmem-memfd.c18
-rw-r--r--block.c440
-rw-r--r--block/commit.c16
-rw-r--r--block/file-posix.c254
-rw-r--r--block/gluster.c10
-rw-r--r--block/mirror.c8
-rw-r--r--block/qapi.c12
-rw-r--r--block/qcow2.c25
-rw-r--r--block/raw-format.c3
-rw-r--r--block/replication.c7
-rw-r--r--block/stream.c21
-rw-r--r--blockdev.c61
-rwxr-xr-xconfigure42
-rw-r--r--docs/devel/decodetree.rst221
-rw-r--r--docs/devel/index.rst2
-rw-r--r--hw/acpi/nvdimm.c18
-rw-r--r--hw/arm/omap2.c2
-rw-r--r--hw/audio/ac97.c2
-rw-r--r--hw/audio/adlib.c2
-rw-r--r--hw/audio/cs4231a.c6
-rw-r--r--hw/audio/es1370.c4
-rw-r--r--hw/audio/gus.c2
-rw-r--r--hw/audio/hda-codec.c18
-rw-r--r--hw/audio/lm4549.c6
-rw-r--r--hw/audio/milkymist-ac97.c2
-rw-r--r--hw/audio/pcspk.c2
-rw-r--r--hw/audio/sb16.c14
-rw-r--r--hw/audio/wm8750.c6
-rw-r--r--hw/block/nvme.c6
-rw-r--r--hw/core/machine.c65
-rw-r--r--hw/display/xlnx_dp.c2
-rw-r--r--hw/i386/acpi-build.c6
-rw-r--r--hw/i386/pc.c57
-rw-r--r--hw/i386/pc_piix.c4
-rw-r--r--hw/i386/pc_q35.c4
-rw-r--r--hw/input/tsc210x.c2
-rw-r--r--hw/sd/Kconfig6
-rw-r--r--hw/sd/Makefile.objs1
-rw-r--r--hw/sd/sdhci-internal.h34
-rw-r--r--hw/sd/sdhci-pci.c87
-rw-r--r--hw/sd/sdhci.c98
-rw-r--r--hw/usb/dev-audio.c2
-rw-r--r--include/block/block.h13
-rw-r--r--include/block/block_int.h14
-rw-r--r--include/hw/boards.h2
-rw-r--r--include/hw/i386/pc.h4
-rw-r--r--include/hw/mem/nvdimm.h10
-rw-r--r--include/qemu/osdep.h13
-rw-r--r--qapi/Makefile.objs6
-rw-r--r--qapi/audio.json304
-rw-r--r--qapi/block-core.json66
-rw-r--r--qapi/qapi-schema.json1
-rw-r--r--qemu-deprecated.texi7
-rw-r--r--qemu-io-cmds.c4
-rw-r--r--qemu-options.hx236
-rwxr-xr-xscripts/decodetree.py333
-rw-r--r--scripts/qemugdb/timers.py6
-rwxr-xr-xscripts/qmp/qmp-shell4
-rw-r--r--target/hppa/cpu.h10
-rw-r--r--target/hppa/gdbstub.c20
-rw-r--r--target/hppa/helper.c10
-rw-r--r--target/hppa/helper.h1
-rw-r--r--target/hppa/insns.decode3
-rw-r--r--target/hppa/mem_helper.c67
-rw-r--r--target/hppa/op_helper.c7
-rw-r--r--target/hppa/trace-events18
-rw-r--r--target/hppa/translate.c51
-rw-r--r--tests/ahci-test.c81
-rwxr-xr-xtests/decode/check.sh6
-rw-r--r--tests/decode/err_pattern_group_empty.decode6
-rw-r--r--tests/decode/err_pattern_group_ident1.decode10
-rw-r--r--tests/decode/err_pattern_group_ident2.decode11
-rw-r--r--tests/decode/err_pattern_group_nest1.decode13
-rw-r--r--tests/decode/err_pattern_group_overlap1.decode6
-rw-r--r--tests/decode/err_width1.decode5
-rw-r--r--tests/decode/err_width2.decode5
-rw-r--r--tests/decode/err_width3.decode5
-rw-r--r--tests/decode/err_width4.decode5
-rw-r--r--tests/decode/succ_pattern_group_nest1.decode22
-rw-r--r--tests/libqos/libqos.c9
-rw-r--r--tests/libqos/libqos.h1
-rwxr-xr-xtests/qemu-iotests/0517
-rw-r--r--tests/qemu-iotests/051.out9
-rw-r--r--tests/qemu-iotests/051.pc.out9
-rwxr-xr-xtests/qemu-iotests/23231
-rw-r--r--tests/qemu-iotests/232.out32
-rw-r--r--tests/qemu-iotests/245991
-rw-r--r--tests/qemu-iotests/245.out5
-rw-r--r--tests/qemu-iotests/group1
-rw-r--r--tests/test-announce-self.c21
-rw-r--r--tests/vhost-user-test.c5
-rw-r--r--tests/virtio-blk-test.c2
-rw-r--r--ui/vnc.c26
-rw-r--r--util/memfd.c10
-rw-r--r--util/oslib-posix.c53
-rw-r--r--util/oslib-win32.c5
-rw-r--r--vl.c7
118 files changed, 4787 insertions, 1909 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index d326756079..564b8dba6e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -117,6 +117,8 @@ F: cpus.c
F: exec.c
F: accel/tcg/
F: accel/stubs/tcg-stub.c
+F: scripts/decodetree.py
+F: docs/devel/decodetree.rst
F: include/exec/cpu*.h
F: include/exec/exec-all.h
F: include/exec/helper*.h
diff --git a/Makefile.objs b/Makefile.objs
index 31a84b7d41..72debbf5c5 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -182,6 +182,7 @@ trace-events-subdirs += qapi
trace-events-subdirs += qom
trace-events-subdirs += scsi
trace-events-subdirs += target/arm
+trace-events-subdirs += target/hppa
trace-events-subdirs += target/i386
trace-events-subdirs += target/mips
trace-events-subdirs += target/ppc
diff --git a/audio/Makefile.objs b/audio/Makefile.objs
index db4fa7f18f..dca87f6347 100644
--- a/audio/Makefile.objs
+++ b/audio/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
+common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
common-obj-$(CONFIG_SPICE) += spiceaudio.o
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 635be73bf4..49e6884309 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -33,28 +33,9 @@
#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;
};
@@ -66,6 +47,7 @@ typedef struct ALSAVoiceOut {
void *pcm_buf;
snd_pcm_t *handle;
struct pollhlp pollhlp;
+ Audiodev *dev;
} ALSAVoiceOut;
typedef struct ALSAVoiceIn {
@@ -73,21 +55,18 @@ typedef struct ALSAVoiceIn {
snd_pcm_t *handle;
void *pcm_buf;
struct pollhlp pollhlp;
+ Audiodev *dev;
} ALSAVoiceIn;
struct alsa_params_req {
int freq;
snd_pcm_format_t fmt;
int nchannels;
- int size_in_usec;
- int override_mask;
- unsigned int buffer_size;
- unsigned int period_size;
};
struct alsa_params_obt {
int freq;
- audfmt_e fmt;
+ AudioFormat fmt;
int endianness;
int nchannels;
snd_pcm_uframes_t samples;
@@ -294,16 +273,16 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
-static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
+static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
{
switch (fmt) {
- case AUD_FMT_S8:
+ case AUDIO_FORMAT_S8:
return SND_PCM_FORMAT_S8;
- case AUD_FMT_U8:
+ case AUDIO_FORMAT_U8:
return SND_PCM_FORMAT_U8;
- case AUD_FMT_S16:
+ case AUDIO_FORMAT_S16:
if (endianness) {
return SND_PCM_FORMAT_S16_BE;
}
@@ -311,7 +290,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
return SND_PCM_FORMAT_S16_LE;
}
- case AUD_FMT_U16:
+ case AUDIO_FORMAT_U16:
if (endianness) {
return SND_PCM_FORMAT_U16_BE;
}
@@ -319,7 +298,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
return SND_PCM_FORMAT_U16_LE;
}
- case AUD_FMT_S32:
+ case AUDIO_FORMAT_S32:
if (endianness) {
return SND_PCM_FORMAT_S32_BE;
}
@@ -327,7 +306,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
return SND_PCM_FORMAT_S32_LE;
}
- case AUD_FMT_U32:
+ case AUDIO_FORMAT_U32:
if (endianness) {
return SND_PCM_FORMAT_U32_BE;
}
@@ -344,58 +323,58 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
}
}
-static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
+static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
int *endianness)
{
switch (alsafmt) {
case SND_PCM_FORMAT_S8:
*endianness = 0;
- *fmt = AUD_FMT_S8;
+ *fmt = AUDIO_FORMAT_S8;
break;
case SND_PCM_FORMAT_U8:
*endianness = 0;
- *fmt = AUD_FMT_U8;
+ *fmt = AUDIO_FORMAT_U8;
break;
case SND_PCM_FORMAT_S16_LE:
*endianness = 0;
- *fmt = AUD_FMT_S16;
+ *fmt = AUDIO_FORMAT_S16;
break;
case SND_PCM_FORMAT_U16_LE:
*endianness = 0;
- *fmt = AUD_FMT_U16;
+ *fmt = AUDIO_FORMAT_U16;
break;
case SND_PCM_FORMAT_S16_BE:
*endianness = 1;
- *fmt = AUD_FMT_S16;
+ *fmt = AUDIO_FORMAT_S16;
break;
case SND_PCM_FORMAT_U16_BE:
*endianness = 1;
- *fmt = AUD_FMT_U16;
+ *fmt = AUDIO_FORMAT_U16;
break;
case SND_PCM_FORMAT_S32_LE:
*endianness = 0;
- *fmt = AUD_FMT_S32;
+ *fmt = AUDIO_FORMAT_S32;
break;
case SND_PCM_FORMAT_U32_LE:
*endianness = 0;
- *fmt = AUD_FMT_U32;
+ *fmt = AUDIO_FORMAT_U32;
break;
case SND_PCM_FORMAT_S32_BE:
*endianness = 1;
- *fmt = AUD_FMT_S32;
+ *fmt = AUDIO_FORMAT_S32;
break;
case SND_PCM_FORMAT_U32_BE:
*endianness = 1;
- *fmt = AUD_FMT_U32;
+ *fmt = AUDIO_FORMAT_U32;
break;
default:
@@ -408,17 +387,18 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
static void alsa_dump_info (struct alsa_params_req *req,
struct alsa_params_obt *obt,
- snd_pcm_format_t obtfmt)
+ snd_pcm_format_t obtfmt,
+ AudiodevAlsaPerDirectionOptions *apdo)
{
- dolog ("parameter | requested value | obtained value\n");
- dolog ("format | %10d | %10d\n", req->fmt, obtfmt);
- dolog ("channels | %10d | %10d\n",
- req->nchannels, obt->nchannels);
- dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
- dolog ("============================================\n");
- dolog ("requested: buffer size %d period size %d\n",
- req->buffer_size, req->period_size);
- dolog ("obtained: samples %ld\n", obt->samples);
+ dolog("parameter | requested value | obtained value\n");
+ dolog("format | %10d | %10d\n", req->fmt, obtfmt);
+ dolog("channels | %10d | %10d\n",
+ req->nchannels, obt->nchannels);
+ dolog("frequency | %10d | %10d\n", req->freq, obt->freq);
+ dolog("============================================\n");
+ dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n",
+ apdo->buffer_length, apdo->period_length);
+ dolog("obtained: samples %ld\n", obt->samples);
}
static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
@@ -451,23 +431,23 @@ 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,
- ALSAConf *conf)
+static int alsa_open(bool in, struct alsa_params_req *req,
+ struct alsa_params_obt *obt, snd_pcm_t **handlep,
+ Audiodev *dev)
{
+ AudiodevAlsaOptions *aopts = &dev->u.alsa;
+ AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->out;
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 = apdo->has_dev ? apdo->dev : "default";
snd_pcm_uframes_t obt_buffer_size;
const char *typ = in ? "ADC" : "DAC";
snd_pcm_format_t obtfmt;
freq = req->freq;
nchannels = req->nchannels;
- size_in_usec = req->size_in_usec;
snd_pcm_hw_params_alloca (&hw_params);
@@ -527,79 +507,42 @@ static int alsa_open (int in, struct alsa_params_req *req,
goto err;
}
- if (req->buffer_size) {
- unsigned long obt;
+ if (apdo->buffer_length) {
+ int dir = 0;
+ unsigned int btime = apdo->buffer_length;
- if (size_in_usec) {
- int dir = 0;
- unsigned int btime = req->buffer_size;
+ err = snd_pcm_hw_params_set_buffer_time_near(
+ handle, hw_params, &btime, &dir);
- err = snd_pcm_hw_params_set_buffer_time_near (
- handle,
- hw_params,
- &btime,
- &dir
- );
- obt = btime;
- }
- else {
- snd_pcm_uframes_t bsize = req->buffer_size;
-
- err = snd_pcm_hw_params_set_buffer_size_near (
- handle,
- hw_params,
- &bsize
- );
- obt = bsize;
- }
if (err < 0) {
- alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
- size_in_usec ? "time" : "size", req->buffer_size);
+ alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n",
+ apdo->buffer_length);
goto err;
}
- if ((req->override_mask & 2) && (obt - req->buffer_size))
- dolog ("Requested buffer %s %u was rejected, using %lu\n",
- size_in_usec ? "time" : "size", req->buffer_size, obt);
+ if (apdo->has_buffer_length && btime != apdo->buffer_length) {
+ dolog("Requested buffer time %" PRId32
+ " was rejected, using %u\n", apdo->buffer_length, btime);
+ }
}
- if (req->period_size) {
- unsigned long obt;
+ if (apdo->period_length) {
+ int dir = 0;
+ unsigned int ptime = apdo->period_length;
- if (size_in_usec) {
- int dir = 0;
- unsigned int ptime = req->period_size;
-
- err = snd_pcm_hw_params_set_period_time_near (
- handle,
- hw_params,
- &ptime,
- &dir
- );
- obt = ptime;
- }
- else {
- int dir = 0;
- snd_pcm_uframes_t psize = req->period_size;
-
- err = snd_pcm_hw_params_set_period_size_near (
- handle,
- hw_params,
- &psize,
- &dir
- );
- obt = psize;
- }
+ err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime,
+ &dir);
if (err < 0) {
- alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
- size_in_usec ? "time" : "size", req->period_size);
+ alsa_logerr2(err, typ, "Failed to set period time to %" PRId32 "\n",
+ apdo->period_length);
goto err;
}
- if (((req->override_mask & 1) && (obt - req->period_size)))
- dolog ("Requested period %s %u was rejected, using %lu\n",
- size_in_usec ? "time" : "size", req->period_size, obt);
+ if (apdo->has_period_length && ptime != apdo->period_length) {
+ dolog("Requested period time %" PRId32 " was rejected, using %d\n",
+ apdo->period_length, ptime);
+ }
}
err = snd_pcm_hw_params (handle, hw_params);
@@ -631,30 +574,12 @@ static int alsa_open (int in, struct alsa_params_req *req,
goto err;
}
- if (!in && conf->threshold) {
- snd_pcm_uframes_t threshold;
- int bytes_per_sec;
-
- bytes_per_sec = freq << (nchannels == 2);
-
- switch (obt->fmt) {
- case AUD_FMT_S8:
- case AUD_FMT_U8:
- break;
-
- case AUD_FMT_S16:
- case AUD_FMT_U16:
- bytes_per_sec <<= 1;
- break;
-
- case AUD_FMT_S32:
- case AUD_FMT_U32:
- bytes_per_sec <<= 2;
- break;
- }
-
- threshold = (conf->threshold * bytes_per_sec) / 1000;
- alsa_set_threshold (handle, threshold);
+ if (!in && aopts->has_threshold && aopts->threshold) {
+ struct audsettings as = { .freq = freq };
+ alsa_set_threshold(
+ handle,
+ audio_buffer_frames(qapi_AudiodevAlsaPerDirectionOptions_base(apdo),
+ &as, aopts->threshold));
}
obt->nchannels = nchannels;
@@ -667,11 +592,11 @@ static int alsa_open (int in, struct alsa_params_req *req,
obt->nchannels != req->nchannels ||
obt->freq != req->freq) {
dolog ("Audio parameters for %s\n", typ);
- alsa_dump_info (req, obt, obtfmt);
+ alsa_dump_info(req, obt, obtfmt, apdo);
}
#ifdef DEBUG
- alsa_dump_info (req, obt, obtfmt);
+ alsa_dump_info(req, obt, obtfmt, pdo);
#endif
return 0;
@@ -797,19 +722,13 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
struct alsa_params_obt obt;
snd_pcm_t *handle;
struct audsettings obt_as;
- ALSAConf *conf = drv_opaque;
+ Audiodev *dev = 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.override_mask =
- (conf->period_size_out_overridden ? 1 : 0) |
- (conf->buffer_size_out_overridden ? 2 : 0);
-
- if (alsa_open (0, &req, &obt, &handle, conf)) {
+
+ if (alsa_open(0, &req, &obt, &handle, dev)) {
return -1;
}
@@ -830,7 +749,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
}
alsa->handle = handle;
- alsa->pollhlp.conf = conf;
+ alsa->dev = dev;
return 0;
}
@@ -870,16 +789,12 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+ AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
switch (cmd) {
case VOICE_ENABLE:
{
- va_list ap;
- int poll_mode;
-
- va_start (ap, cmd);
- poll_mode = va_arg (ap, int);
- va_end (ap);
+ bool poll_mode = apdo->try_poll;
ldebug ("enabling voice\n");
if (poll_mode && alsa_poll_out (hw)) {
@@ -908,19 +823,13 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
struct alsa_params_obt obt;
snd_pcm_t *handle;
struct audsettings obt_as;
- ALSAConf *conf = drv_opaque;
+ Audiodev *dev = 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.override_mask =
- (conf->period_size_in_overridden ? 1 : 0) |
- (conf->buffer_size_in_overridden ? 2 : 0);
-
- if (alsa_open (1, &req, &obt, &handle, conf)) {
+
+ if (alsa_open(1, &req, &obt, &handle, dev)) {
return -1;
}
@@ -941,7 +850,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}
alsa->handle = handle;
- alsa->pollhlp.conf = conf;
+ alsa->dev = dev;
return 0;
}
@@ -1083,16 +992,12 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size)
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+ AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
switch (cmd) {
case VOICE_ENABLE:
{
- va_list ap;
- int poll_mode;
-
- va_start (ap, cmd);
- poll_mode = va_arg (ap, int);
- va_end (ap);
+ bool poll_mode = apdo->try_poll;
ldebug ("enabling voice\n");
if (poll_mode && alsa_poll_in (hw)) {
@@ -1115,88 +1020,54 @@ 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_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
+{
+ if (!apdo->has_try_poll) {
+ apdo->try_poll = true;
+ apdo->has_try_poll = true;
+ }
+}
-static void *alsa_audio_init (void)
+static void *alsa_audio_init(Audiodev *dev)
{
- ALSAConf *conf = g_malloc(sizeof(ALSAConf));
- *conf = glob_conf;
- return conf;
+ AudiodevAlsaOptions *aopts;
+ assert(dev->driver == AUDIODEV_DRIVER_ALSA);
+
+ aopts = &dev->u.alsa;
+ alsa_init_per_direction(aopts->in);
+ alsa_init_per_direction(aopts->out);
+
+ /*
+ * need to define them, as otherwise alsa produces no sound
+ * doesn't set has_* so alsa_open can identify it wasn't set by the user
+ */
+ if (!dev->u.alsa.out->has_period_length) {
+ /* 1024 frames assuming 44100Hz */
+ dev->u.alsa.out->period_length = 1024 * 1000000 / 44100;
+ }
+ if (!dev->u.alsa.out->has_buffer_length) {
+ /* 4096 frames assuming 44100Hz */
+ dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100;
+ }
+
+ /*
+ * OptsVisitor sets unspecified optional fields to zero, but do not depend
+ * on it...
+ */
+ if (!dev->u.alsa.in->has_period_length) {
+ dev->u.alsa.in->period_length = 0;
+ }
+ if (!dev->u.alsa.in->has_buffer_length) {
+ dev->u.alsa.in->buffer_length = 0;
+ }
+
+ return dev;
}
static void alsa_audio_fini (void *opaque)
{
- g_free(opaque);
}
-static struct audio_option alsa_options[] = {
- {
- .name = "DAC_SIZE_IN_USEC",
- .tag = AUD_OPT_BOOL,
- .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 = &glob_conf.period_size_out,
- .descr = "DAC period size (0 to go with system default)",
- .overriddenp = &glob_conf.period_size_out_overridden
- },
- {
- .name = "DAC_BUFFER_SIZE",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.buffer_size_out,
- .descr = "DAC buffer size (0 to go with system default)",
- .overriddenp = &glob_conf.buffer_size_out_overridden
- },
- {
- .name = "ADC_SIZE_IN_USEC",
- .tag = AUD_OPT_BOOL,
- .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 = &glob_conf.period_size_in,
- .descr = "ADC period size (0 to go with system default)",
- .overriddenp = &glob_conf.period_size_in_overridden
- },
- {
- .name = "ADC_BUFFER_SIZE",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.buffer_size_in,
- .descr = "ADC buffer size (0 to go with system default)",
- .overriddenp = &glob_conf.buffer_size_in_overridden
- },
- {
- .name = "THRESHOLD",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.threshold,
- .descr = "(undocumented)"
- },
- {
- .name = "DAC_DEV",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.pcm_name_out,
- .descr = "DAC device name (for instance dmix)"
- },
- {
- .name = "ADC_DEV",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.pcm_name_in,
- .descr = "ADC device name"
- },
- { /* End of list */ }
-};
-
static struct audio_pcm_ops alsa_pcm_ops = {
.init_out = alsa_init_out,
.fini_out = alsa_fini_out,
@@ -1214,7 +1085,6 @@ static struct audio_pcm_ops alsa_pcm_ops = {
static struct audio_driver alsa_audio_driver = {
.name = "alsa",
.descr = "ALSA http://www.alsa-project.org",
- .options = alsa_options,
.init = alsa_audio_init,
.fini = alsa_audio_fini,
.pcm_ops = &alsa_pcm_ops,
diff --git a/audio/audio.c b/audio/audio.c
index 909c817103..5fd9a58a80 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -26,6 +26,9 @@
#include "audio.h"
#include "monitor/monitor.h"
#include "qemu/timer.h"
+#include "qapi/error.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-audio.h"
#include "sysemu/sysemu.h"
#include "qemu/cutils.h"
#include "sysemu/replay.h"
@@ -46,14 +49,16 @@
The 1st one is the one used by default, that is the reason
that we generate the list.
*/
-static const char *audio_prio_list[] = {
+const char *audio_prio_list[] = {
"spice",
CONFIG_AUDIO_DRIVERS
"none",
"wav",
+ NULL
};
static QLIST_HEAD(, audio_driver) audio_drivers;
+static AudiodevListHead audiodevs = QSIMPLEQ_HEAD_INITIALIZER(audiodevs);
void audio_driver_register(audio_driver *drv)
{
@@ -80,61 +85,6 @@ audio_driver *audio_driver_lookup(const char *name)
return NULL;
}
-static void audio_module_load_all(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(audio_prio_list); i++) {
- audio_driver_lookup(audio_prio_list[i]);
- }
-}
-
-struct fixed_settings {
- int enabled;
- int nb_voices;
- int greedy;
- struct audsettings settings;
-};
-
-static struct {
- struct fixed_settings fixed_out;
- struct fixed_settings fixed_in;
- union {
- int hertz;
- int64_t ticks;
- } period;
- int try_poll_in;
- int try_poll_out;
-} conf = {
- .fixed_out = { /* DAC fixed settings */
- .enabled = 1,
- .nb_voices = 1,
- .greedy = 1,
- .settings = {
- .freq = 44100,
- .nchannels = 2,
- .fmt = AUD_FMT_S16,
- .endianness = AUDIO_HOST_ENDIANNESS,
- }
- },
-
- .fixed_in = { /* ADC fixed settings */
- .enabled = 1,
- .nb_voices = 1,
- .greedy = 1,
- .settings = {
- .freq = 44100,
- .nchannels = 2,
- .fmt = AUD_FMT_S16,
- .endianness = AUDIO_HOST_ENDIANNESS,
- }
- },
-
- .period = { .hertz = 100 },
- .try_poll_in = 1,
- .try_poll_out = 1,
-};
-
static AudioState glob_audio_state;
const struct mixeng_volume nominal_volume = {
@@ -151,9 +101,6 @@ const struct mixeng_volume nominal_volume = {
#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
#error No its not
#else
-static void audio_print_options (const char *prefix,
- struct audio_option *opt);
-
int audio_bug (const char *funcname, int cond)
{
if (cond) {
@@ -161,16 +108,9 @@ int audio_bug (const char *funcname, int cond)
AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
if (!shown) {
- struct audio_driver *d;
-
shown = 1;
AUD_log (NULL, "Save all your work and restart without audio\n");
- AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n");
AUD_log (NULL, "I am sorry\n");
- d = glob_audio_state.drv;
- if (d) {
- audio_print_options (d->name, d->options);
- }
}
AUD_log (NULL, "Context:\n");
@@ -232,135 +172,6 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size)
return g_malloc0 (len);
}
-static char *audio_alloc_prefix (const char *s)
-{
- const char qemu_prefix[] = "QEMU_";
- size_t len, i;
- char *r, *u;
-
- if (!s) {
- return NULL;
- }
-
- len = strlen (s);
- r = g_malloc (len + sizeof (qemu_prefix));
-
- u = r + sizeof (qemu_prefix) - 1;
-
- pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix);
- pstrcat (r, len + sizeof (qemu_prefix), s);
-
- for (i = 0; i < len; ++i) {
- u[i] = qemu_toupper(u[i]);
- }
-
- return r;
-}
-
-static const char *audio_audfmt_to_string (audfmt_e fmt)
-{
- switch (fmt) {
- case AUD_FMT_U8:
- return "U8";
-
- case AUD_FMT_U16:
- return "U16";
-
- case AUD_FMT_S8:
- return "S8";
-
- case AUD_FMT_S16:
- return "S16";
-
- case AUD_FMT_U32:
- return "U32";
-
- case AUD_FMT_S32:
- return "S32";
- }
-
- dolog ("Bogus audfmt %d returning S16\n", fmt);
- return "S16";
-}
-
-static audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval,
- int *defaultp)
-{
- if (!strcasecmp (s, "u8")) {
- *defaultp = 0;
- return AUD_FMT_U8;
- }
- else if (!strcasecmp (s, "u16")) {
- *defaultp = 0;
- return AUD_FMT_U16;
- }
- else if (!strcasecmp (s, "u32")) {
- *defaultp = 0;
- return AUD_FMT_U32;
- }
- else if (!strcasecmp (s, "s8")) {
- *defaultp = 0;
- return AUD_FMT_S8;
- }
- else if (!strcasecmp (s, "s16")) {
- *defaultp = 0;
- return AUD_FMT_S16;
- }
- else if (!strcasecmp (s, "s32")) {
- *defaultp = 0;
- return AUD_FMT_S32;
- }
- else {
- dolog ("Bogus audio format `%s' using %s\n",
- s, audio_audfmt_to_string (defval));
- *defaultp = 1;
- return defval;
- }
-}
-
-static audfmt_e audio_get_conf_fmt (const char *envname,
- audfmt_e defval,
- int *defaultp)
-{
- const char *var = getenv (envname);
- if (!var) {
- *defaultp = 1;
- return defval;
- }
- return audio_string_to_audfmt (var, defval, defaultp);
-}
-
-static int audio_get_conf_int (const char *key, int defval, int *defaultp)
-{
- int val;
- char *strval;
-
- strval = getenv (key);
- if (strval && !qemu_strtoi(strval, NULL, 10, &val)) {
- *defaultp = 0;
- return val;
- }
- else {
- *defaultp = 1;
- return defval;
- }
-}
-
-static const char *audio_get_conf_str (const char *key,
- const char *defval,
- int *defaultp)
-{
- const char *val = getenv (key);
- if (!val) {
- *defaultp = 1;
- return defval;
- }
- else {
- *defaultp = 0;
- return val;
- }
-}
-
void AUD_vlog (const char *cap, const char *fmt, va_list ap)
{
if (cap) {
@@ -379,167 +190,27 @@ void AUD_log (const char *cap, const char *fmt, ...)
va_end (ap);
}
-static void audio_print_options (const char *prefix,
- struct audio_option *opt)
-{
- char *uprefix;
-
- if (!prefix) {
- dolog ("No prefix specified\n");
- return;
- }
-
- if (!opt) {
- dolog ("No options\n");
- return;
- }
-
- uprefix = audio_alloc_prefix (prefix);
-
- for (; opt->name; opt++) {
- const char *state = "default";
- printf (" %s_%s: ", uprefix, opt->name);
-
- if (opt->overriddenp && *opt->overriddenp) {
- state = "current";
- }
-
- switch (opt->tag) {
- case AUD_OPT_BOOL:
- {
- int *intp = opt->valp;
- printf ("boolean, %s = %d\n", state, *intp ? 1 : 0);
- }
- break;
-
- case AUD_OPT_INT:
- {
- int *intp = opt->valp;
- printf ("integer, %s = %d\n", state, *intp);
- }
- break;
-
- case AUD_OPT_FMT:
- {
- audfmt_e *fmtp = opt->valp;
- printf (
- "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n",
- state,
- audio_audfmt_to_string (*fmtp)
- );
- }
- break;
-
- case AUD_OPT_STR:
- {
- const char **strp = opt->valp;
- printf ("string, %s = %s\n",
- state,
- *strp ? *strp : "(not set)");
- }
- break;
-
- default:
- printf ("???\n");
- dolog ("Bad value tag for option %s_%s %d\n",
- uprefix, opt->name, opt->tag);
- break;
- }
- printf (" %s\n", opt->descr);
- }
-
- g_free (uprefix);
-}
-
-static void audio_process_options (const char *prefix,
- struct audio_option *opt)
-{
- gchar *prefix_upper;
-
- if (audio_bug(__func__, !prefix)) {
- dolog ("prefix = NULL\n");
- return;
- }
-
- if (audio_bug(__func__, !opt)) {
- dolog ("opt = NULL\n");
- return;
- }
-
- prefix_upper = g_utf8_strup(prefix, -1);
-
- for (; opt->name; opt++) {
- char *optname;
- int def;
-
- if (!opt->valp) {
- dolog ("Option value pointer for `%s' is not set\n",
- opt->name);
- continue;
- }
-
- optname = g_strdup_printf("QEMU_%s_%s", prefix_upper, opt->name);
-
- def = 1;
- switch (opt->tag) {
- case AUD_OPT_BOOL:
- case AUD_OPT_INT:
- {
- int *intp = opt->valp;
- *intp = audio_get_conf_int (optname, *intp, &def);
- }
- break;
-
- case AUD_OPT_FMT:
- {
- audfmt_e *fmtp = opt->valp;
- *fmtp = audio_get_conf_fmt (optname, *fmtp, &def);
- }
- break;
-
- case AUD_OPT_STR:
- {
- const char **strp = opt->valp;
- *strp = audio_get_conf_str (optname, *strp, &def);
- }
- break;
-
- default:
- dolog ("Bad value tag for option `%s' - %d\n",
- optname, opt->tag);
- break;
- }
-
- if (!opt->overriddenp) {
- opt->overriddenp = &opt->overridden;
- }
- *opt->overriddenp = !def;
- g_free (optname);
- }
- g_free(prefix_upper);
-}
-
static void audio_print_settings (struct audsettings *as)
{
dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);
switch (as->fmt) {
- case AUD_FMT_S8:
+ case AUDIO_FORMAT_S8:
AUD_log (NULL, "S8");
break;
- case AUD_FMT_U8:
+ case AUDIO_FORMAT_U8:
AUD_log (NULL, "U8");
break;
- case AUD_FMT_S16:
+ case AUDIO_FORMAT_S16:
AUD_log (NULL, "S16");
break;
- case AUD_FMT_U16:
+ case AUDIO_FORMAT_U16:
AUD_log (NULL, "U16");
break;
- case AUD_FMT_S32:
+ case AUDIO_FORMAT_S32:
AUD_log (NULL, "S32");
break;
- case AUD_FMT_U32:
+ case AUDIO_FORMAT_U32:
AUD_log (NULL, "U32");
break;
default:
@@ -570,12 +241,12 @@ static int audio_validate_settings (struct audsettings *as)
invalid |= as->endianness != 0 && as->endianness != 1;
switch (as->fmt) {
- case AUD_FMT_S8:
- case AUD_FMT_U8:
- case AUD_FMT_S16:
- case AUD_FMT_U16:
- case AUD_FMT_S32:
- case AUD_FMT_U32:
+ case AUDIO_FORMAT_S8:
+ case AUDIO_FORMAT_U8:
+ case AUDIO_FORMAT_S16:
+ case AUDIO_FORMAT_U16:
+ case AUDIO_FORMAT_S32:
+ case AUDIO_FORMAT_U32:
break;
default:
invalid = 1;
@@ -591,25 +262,28 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a
int bits = 8, sign = 0;
switch (as->fmt) {
- case AUD_FMT_S8:
+ case AUDIO_FORMAT_S8:
sign = 1;
/* fall through */
- case AUD_FMT_U8:
+ case AUDIO_FORMAT_U8:
break;
- case AUD_FMT_S16:
+ case AUDIO_FORMAT_S16:
sign = 1;
/* fall through */
- case AUD_FMT_U16:
+ case AUDIO_FORMAT_U16:
bits = 16;
break;
- case AUD_FMT_S32:
+ case AUDIO_FORMAT_S32:
sign = 1;
/* fall through */
- case AUD_FMT_U32:
+ case AUDIO_FORMAT_U32:
bits = 32;
break;
+
+ default:
+ abort();
}
return info->freq == as->freq
&& info->nchannels == as->nchannels
@@ -623,24 +297,27 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
int bits = 8, sign = 0, shift = 0;
switch (as->fmt) {
- case AUD_FMT_S8:
+ case AUDIO_FORMAT_S8:
sign = 1;
- case AUD_FMT_U8:
+ case AUDIO_FORMAT_U8:
break;
- case AUD_FMT_S16:
+ case AUDIO_FORMAT_S16:
sign = 1;
- case AUD_FMT_U16:
+ case AUDIO_FORMAT_U16:
bits = 16;
shift = 1;
break;
- case AUD_FMT_S32:
+ case AUDIO_FORMAT_S32:
sign = 1;
- case AUD_FMT_U32:
+ case AUDIO_FORMAT_U32:
bits = 32;
shift = 2;
break;
+
+ default:
+ abort();
}
info->freq = as->freq;
@@ -1132,11 +809,11 @@ static void audio_reset_timer (AudioState *s)
{
if (audio_is_timer_needed ()) {
timer_mod_anticipate_ns(s->ts,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks);
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks);
if (!audio_timer_running) {
audio_timer_running = true;
audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- trace_audio_timer_start(conf.period.ticks / SCALE_MS);
+ trace_audio_timer_start(s->period_ticks / SCALE_MS);
}
} else {
timer_del(s->ts);
@@ -1150,16 +827,17 @@ static void audio_reset_timer (AudioState *s)
static void audio_timer (void *opaque)
{
int64_t now, diff;
+ AudioState *s = opaque;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
diff = now - audio_timer_last;
- if (diff > conf.period.ticks * 3 / 2) {
+ if (diff > s->period_ticks * 3 / 2) {
trace_audio_timer_delayed(diff / SCALE_MS);
}
audio_timer_last = now;
- audio_run ("timer");
- audio_reset_timer (opaque);
+ audio_run("timer");
+ audio_reset_timer(s);
}
/*
@@ -1219,7 +897,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
if (!hw->enabled) {
hw->enabled = 1;
if (s->vm_running) {
- hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out);
+ hw->pcm_ops->ctl_out(hw, VOICE_ENABLE);
audio_reset_timer (s);
}
}
@@ -1264,7 +942,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
if (!hw->enabled) {
hw->enabled = 1;
if (s->vm_running) {
- hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in);
+ hw->pcm_ops->ctl_in(hw, VOICE_ENABLE);
audio_reset_timer (s);
}
}
@@ -1585,169 +1263,10 @@ void audio_run (const char *msg)
#endif
}
-static struct audio_option audio_options[] = {
- /* DAC */
- {
- .name = "DAC_FIXED_SETTINGS",
- .tag = AUD_OPT_BOOL,
- .valp = &conf.fixed_out.enabled,
- .descr = "Use fixed settings for host DAC"
- },
- {
- .name = "DAC_FIXED_FREQ",
- .tag = AUD_OPT_INT,
- .valp = &conf.fixed_out.settings.freq,
- .descr = "Frequency for fixed host DAC"
- },
- {
- .name = "DAC_FIXED_FMT",
- .tag = AUD_OPT_FMT,
- .valp = &conf.fixed_out.settings.fmt,
- .descr = "Format for fixed host DAC"
- },
- {
- .name = "DAC_FIXED_CHANNELS",
- .tag = AUD_OPT_INT,
- .valp = &conf.fixed_out.settings.nchannels,
- .descr = "Number of channels for fixed DAC (1 - mono, 2 - stereo)"
- },
- {
- .name = "DAC_VOICES",
- .tag = AUD_OPT_INT,
- .valp = &conf.fixed_out.nb_voices,
- .descr = "Number of voices for DAC"
- },
- {
- .name = "DAC_TRY_POLL",
- .tag = AUD_OPT_BOOL,
- .valp = &conf.try_poll_out,
- .descr = "Attempt using poll mode for DAC"
- },
- /* ADC */
- {
- .name = "ADC_FIXED_SETTINGS",
- .tag = AUD_OPT_BOOL,
- .valp = &conf.fixed_in.enabled,
- .descr = "Use fixed settings for host ADC"
- },
- {
- .name = "ADC_FIXED_FREQ",
- .tag = AUD_OPT_INT,
- .valp = &conf.fixed_in.settings.freq,
- .descr = "Frequency for fixed host ADC"
- },
- {
- .name = "ADC_FIXED_FMT",
- .tag = AUD_OPT_FMT,
- .valp = &conf.fixed_in.settings.fmt,
- .descr = "Format for fixed host ADC"
- },
- {
- .name = "ADC_FIXED_CHANNELS",
- .tag = AUD_OPT_INT,
- .valp = &conf.fixed_in.settings.nchannels,
- .descr = "Number of channels for fixed ADC (1 - mono, 2 - stereo)"
- },
- {
- .name = "ADC_VOICES",
- .tag = AUD_OPT_INT,
- .valp = &conf.fixed_in.nb_voices,
- .descr = "Number of voices for ADC"
- },
- {
- .name = "ADC_TRY_POLL",
- .tag = AUD_OPT_BOOL,
- .valp = &conf.try_poll_in,
- .descr = "Attempt using poll mode for ADC"
- },
- /* Misc */
- {
- .name = "TIMER_PERIOD",
- .tag = AUD_OPT_INT,
- .valp = &conf.period.hertz,
- .descr = "Timer period in HZ (0 - use lowest possible)"
- },
- { /* End of list */ }
-};
-
-static void audio_pp_nb_voices (const char *typ, int nb)
-{
- switch (nb) {
- case 0:
- printf ("Does not support %s\n", typ);
- break;
- case 1:
- printf ("One %s voice\n", typ);
- break;
- case INT_MAX:
- printf ("Theoretically supports many %s voices\n", typ);
- break;
- default:
- printf ("Theoretically supports up to %d %s voices\n", nb, typ);
- break;
- }
-
-}
-
-void AUD_help (void)
-{
- struct audio_driver *d;
-
- /* make sure we print the help text for modular drivers too */
- audio_module_load_all();
-
- audio_process_options ("AUDIO", audio_options);
- QLIST_FOREACH(d, &audio_drivers, next) {
- if (d->options) {
- audio_process_options (d->name, d->options);
- }
- }
-
- printf ("Audio options:\n");
- audio_print_options ("AUDIO", audio_options);
- printf ("\n");
-
- printf ("Available drivers:\n");
-
- QLIST_FOREACH(d, &audio_drivers, next) {
-
- printf ("Name: %s\n", d->name);
- printf ("Description: %s\n", d->descr);
-
- audio_pp_nb_voices ("playback", d->max_voices_out);
- audio_pp_nb_voices ("capture", d->max_voices_in);
-
- if (d->options) {
- printf ("Options:\n");
- audio_print_options (d->name, d->options);
- }
- else {
- printf ("No options\n");
- }
- printf ("\n");
- }
-
- printf (
- "Options are settable through environment variables.\n"
- "Example:\n"
-#ifdef _WIN32
- " set QEMU_AUDIO_DRV=wav\n"
- " set QEMU_WAV_PATH=c:\\tune.wav\n"
-#else
- " export QEMU_AUDIO_DRV=wav\n"
- " export QEMU_WAV_PATH=$HOME/tune.wav\n"
- "(for csh replace export with setenv in the above)\n"
-#endif
- " qemu ...\n\n"
- );
-}
-
-static int audio_driver_init(AudioState *s, struct audio_driver *drv, bool msg)
+static int audio_driver_init(AudioState *s, struct audio_driver *drv,
+ bool msg, Audiodev *dev)
{
- if (drv->options) {
- audio_process_options (drv->name, drv->options);
- }
- s->drv_opaque = drv->init ();
+ s->drv_opaque = drv->init(dev);
if (s->drv_opaque) {
audio_init_nb_voices_out (drv);
@@ -1773,11 +1292,11 @@ static void audio_vm_change_state_handler (void *opaque, int running,
s->vm_running = running;
while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
- hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out);
+ hwo->pcm_ops->ctl_out(hwo, op);
}
while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
- hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in);
+ hwi->pcm_ops->ctl_in(hwi, op);
}
audio_reset_timer (s);
}
@@ -1827,6 +1346,11 @@ void audio_cleanup(void)
s->drv->fini (s->drv_opaque);
s->drv = NULL;
}
+
+ if (s->dev) {
+ qapi_free_Audiodev(s->dev);
+ s->dev = NULL;
+ }
}
static const VMStateDescription vmstate_audio = {
@@ -1838,19 +1362,58 @@ static const VMStateDescription vmstate_audio = {
}
};
-static void audio_init (void)
+static void audio_validate_opts(Audiodev *dev, Error **errp);
+
+static AudiodevListEntry *audiodev_find(
+ AudiodevListHead *head, const char *drvname)
+{
+ AudiodevListEntry *e;
+ QSIMPLEQ_FOREACH(e, head, next) {
+ if (strcmp(AudiodevDriver_str(e->dev->driver), drvname) == 0) {
+ return e;
+ }
+ }
+
+ return NULL;
+}
+
+static int audio_init(Audiodev *dev)
{
size_t i;
int done = 0;
- const char *drvname;
+ const char *drvname = NULL;
VMChangeStateEntry *e;
AudioState *s = &glob_audio_state;
struct audio_driver *driver;
+ /* silence gcc warning about uninitialized variable */
+ AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head);
if (s->drv) {
- return;
+ if (dev) {
+ dolog("Cannot create more than one audio backend, sorry\n");
+ qapi_free_Audiodev(dev);
+ }
+ return -1;
}
+ if (dev) {
+ /* -audiodev option */
+ drvname = AudiodevDriver_str(dev->driver);
+ } else {
+ /* legacy implicit initialization */
+ head = audio_handle_legacy_opts();
+ /*
+ * In case of legacy initialization, all Audiodevs in the list will have
+ * the same configuration (except the driver), so it does't matter which
+ * one we chose. We need an Audiodev to set up AudioState before we can
+ * init a driver. Also note that dev at this point is still in the
+ * list.
+ */
+ dev = QSIMPLEQ_FIRST(&head)->dev;
+ audio_validate_opts(dev, &error_abort);
+ }
+ s->dev = dev;
+
QLIST_INIT (&s->hw_head_out);
QLIST_INIT (&s->hw_head_in);
QLIST_INIT (&s->cap_head);
@@ -1858,10 +1421,8 @@ static void audio_init (void)
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
- audio_process_options ("AUDIO", audio_options);
-
- s->nb_hw_voices_out = conf.fixed_out.nb_voices;
- s->nb_hw_voices_in = conf.fixed_in.nb_voices;
+ s->nb_hw_voices_out = audio_get_pdo_out(dev)->voices;
+ s->nb_hw_voices_in = audio_get_pdo_in(dev)->voices;
if (s->nb_hw_voices_out <= 0) {
dolog ("Bogus number of playback voices %d, setting to 1\n",
@@ -1875,46 +1436,42 @@ static void audio_init (void)
s->nb_hw_voices_in = 0;
}
- {
- int def;
- drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def);
- }
-
if (drvname) {
driver = audio_driver_lookup(drvname);
if (driver) {
- done = !audio_driver_init(s, driver, true);
+ done = !audio_driver_init(s, driver, true, dev);
} else {
dolog ("Unknown audio driver `%s'\n", drvname);
- dolog ("Run with -audio-help to list available drivers\n");
}
- }
-
- if (!done) {
- for (i = 0; !done && i < ARRAY_SIZE(audio_prio_list); i++) {
+ } else {
+ for (i = 0; audio_prio_list[i]; i++) {
+ AudiodevListEntry *e = audiodev_find(&head, audio_prio_list[i]);
driver = audio_driver_lookup(audio_prio_list[i]);
- if (driver && driver->can_be_default) {
- done = !audio_driver_init(s, driver, false);
+
+ if (e && driver) {
+ s->dev = dev = e->dev;
+ audio_validate_opts(dev, &error_abort);
+ done = !audio_driver_init(s, driver, false, dev);
+ if (done) {
+ e->dev = NULL;
+ break;
+ }
}
}
}
+ audio_free_audiodev_list(&head);
if (!done) {
driver = audio_driver_lookup("none");
- done = !audio_driver_init(s, driver, false);
+ done = !audio_driver_init(s, driver, false, dev);
assert(done);
dolog("warning: Using timer based audio emulation\n");
}
- if (conf.period.hertz <= 0) {
- if (conf.period.hertz < 0) {
- dolog ("warning: Timer period is negative - %d "
- "treating as zero\n",
- conf.period.hertz);
- }
- conf.period.ticks = 1;
+ if (dev->timer_period <= 0) {
+ s->period_ticks = 1;
} else {
- conf.period.ticks = NANOSECONDS_PER_SECOND / conf.period.hertz;
+ s->period_ticks = NANOSECONDS_PER_SECOND / dev->timer_period;
}
e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
@@ -1925,11 +1482,22 @@ static void audio_init (void)
QLIST_INIT (&s->card_head);
vmstate_register (NULL, 0, &vmstate_audio, s);
+ return 0;
+}
+
+void audio_free_audiodev_list(AudiodevListHead *head)
+{
+ AudiodevListEntry *e;
+ while ((e = QSIMPLEQ_FIRST(head))) {
+ QSIMPLEQ_REMOVE_HEAD(head, next);
+ qapi_free_Audiodev(e->dev);
+ g_free(e);
+ }
}
void AUD_register_card (const char *name, QEMUSoundCard *card)
{
- audio_init ();
+ audio_init(NULL);
card->name = g_strdup (name);
memset (&card->entries, 0, sizeof (card->entries));
QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries);
@@ -2069,3 +1637,174 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
}
}
}
+
+void audio_create_pdos(Audiodev *dev)
+{
+ switch (dev->driver) {
+#define CASE(DRIVER, driver, pdo_name) \
+ case AUDIODEV_DRIVER_##DRIVER: \
+ if (!dev->u.driver.has_in) { \
+ dev->u.driver.in = g_malloc0( \
+ sizeof(Audiodev##pdo_name##PerDirectionOptions)); \
+ dev->u.driver.has_in = true; \
+ } \
+ if (!dev->u.driver.has_out) { \
+ dev->u.driver.out = g_malloc0( \
+ sizeof(AudiodevAlsaPerDirectionOptions)); \
+ dev->u.driver.has_out = true; \
+ } \
+ break
+
+ CASE(NONE, none, );
+ CASE(ALSA, alsa, Alsa);
+ CASE(COREAUDIO, coreaudio, Coreaudio);
+ CASE(DSOUND, dsound, );
+ CASE(OSS, oss, Oss);
+ CASE(PA, pa, Pa);
+ CASE(SDL, sdl, );
+ CASE(SPICE, spice, );
+ CASE(WAV, wav, );
+
+ case AUDIODEV_DRIVER__MAX:
+ abort();
+ };
+}
+
+static void audio_validate_per_direction_opts(
+ AudiodevPerDirectionOptions *pdo, Error **errp)
+{
+ if (!pdo->has_fixed_settings) {
+ pdo->has_fixed_settings = true;
+ pdo->fixed_settings = true;
+ }
+ if (!pdo->fixed_settings &&
+ (pdo->has_frequency || pdo->has_channels || pdo->has_format)) {
+ error_setg(errp,
+ "You can't use frequency, channels or format with fixed-settings=off");
+ return;
+ }
+
+ if (!pdo->has_frequency) {
+ pdo->has_frequency = true;
+ pdo->frequency = 44100;
+ }
+ if (!pdo->has_channels) {
+ pdo->has_channels = true;
+ pdo->channels = 2;
+ }
+ if (!pdo->has_voices) {
+ pdo->has_voices = true;
+ pdo->voices = 1;
+ }
+ if (!pdo->has_format) {
+ pdo->has_format = true;
+ pdo->format = AUDIO_FORMAT_S16;
+ }
+}
+
+static void audio_validate_opts(Audiodev *dev, Error **errp)
+{
+ Error *err = NULL;
+
+ audio_create_pdos(dev);
+
+ audio_validate_per_direction_opts(audio_get_pdo_in(dev), &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ audio_validate_per_direction_opts(audio_get_pdo_out(dev), &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ if (!dev->has_timer_period) {
+ dev->has_timer_period = true;
+ dev->timer_period = 10000; /* 100Hz -> 10ms */
+ }
+}
+
+void audio_parse_option(const char *opt)
+{
+ AudiodevListEntry *e;
+ Audiodev *dev = NULL;
+
+ Visitor *v = qobject_input_visitor_new_str(opt, "driver", &error_fatal);
+ visit_type_Audiodev(v, NULL, &dev, &error_fatal);
+ visit_free(v);
+
+ audio_validate_opts(dev, &error_fatal);
+
+ e = g_malloc0(sizeof(AudiodevListEntry));
+ e->dev = dev;
+ QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next);
+}
+
+void audio_init_audiodevs(void)
+{
+ AudiodevListEntry *e;
+
+ QSIMPLEQ_FOREACH(e, &audiodevs, next) {
+ audio_init(e->dev);
+ }
+}
+
+audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)
+{
+ return (audsettings) {
+ .freq = pdo->frequency,
+ .nchannels = pdo->channels,
+ .fmt = pdo->format,
+ .endianness = AUDIO_HOST_ENDIANNESS,
+ };
+}
+
+int audioformat_bytes_per_sample(AudioFormat fmt)
+{
+ switch (fmt) {
+ case AUDIO_FORMAT_U8:
+ case AUDIO_FORMAT_S8:
+ return 1;
+
+ case AUDIO_FORMAT_U16:
+ case AUDIO_FORMAT_S16:
+ return 2;
+
+ case AUDIO_FORMAT_U32:
+ case AUDIO_FORMAT_S32:
+ return 4;
+
+ case AUDIO_FORMAT__MAX:
+ ;
+ }
+ abort();
+}
+
+
+/* frames = freq * usec / 1e6 */
+int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
+ audsettings *as, int def_usecs)
+{
+ uint64_t usecs = pdo->has_buffer_length ? pdo->buffer_length : def_usecs;
+ return (as->freq * usecs + 500000) / 1000000;
+}
+
+/* samples = channels * frames = channels * freq * usec / 1e6 */
+int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
+ audsettings *as, int def_usecs)
+{
+ return as->nchannels * audio_buffer_frames(pdo, as, def_usecs);
+}
+
+/*
+ * bytes = bytes_per_sample * samples =
+ * bytes_per_sample * channels * freq * usec / 1e6
+ */
+int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
+ audsettings *as, int def_usecs)
+{
+ return audio_buffer_samples(pdo, as, def_usecs) *
+ audioformat_bytes_per_sample(as->fmt);
+}
diff --git a/audio/audio.h b/audio/audio.h
index f4339a185e..64b0f761bc 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -26,30 +26,31 @@
#define QEMU_AUDIO_H
#include "qemu/queue.h"
+#include "qapi/qapi-types-audio.h"
typedef void (*audio_callback_fn) (void *opaque, int avail);
-typedef enum {
- AUD_FMT_U8,
- AUD_FMT_S8,
- AUD_FMT_U16,
- AUD_FMT_S16,
- AUD_FMT_U32,
- AUD_FMT_S32
-} audfmt_e;
-
#ifdef HOST_WORDS_BIGENDIAN
#define AUDIO_HOST_ENDIANNESS 1
#else
#define AUDIO_HOST_ENDIANNESS 0
#endif
-struct audsettings {
+typedef struct audsettings {
int freq;
int nchannels;
- audfmt_e fmt;
+ AudioFormat fmt;
int endianness;
-};
+} audsettings;
+
+audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
+int audioformat_bytes_per_sample(AudioFormat fmt);
+int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
+ audsettings *as, int def_usecs);
+int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
+ audsettings *as, int def_usecs);
+int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
+ audsettings *as, int def_usecs);
typedef enum {
AUD_CNOTIFY_ENABLE,
@@ -89,7 +90,6 @@ typedef struct QEMUAudioTimeStamp {
void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
-void AUD_help (void);
void AUD_register_card (const char *name, QEMUSoundCard *card);
void AUD_remove_card (QEMUSoundCard *card);
CaptureVoiceOut *AUD_add_capture (
@@ -171,4 +171,8 @@ void audio_sample_to_uint64(void *samples, int pos,
void audio_sample_from_uint64(void *samples, int pos,
uint64_t left, uint64_t right);
+void audio_parse_option(const char *opt);
+void audio_init_audiodevs(void);
+void audio_legacy_help(void);
+
#endif /* QEMU_AUDIO_H */
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 6c451b995c..3f14842709 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -33,22 +33,6 @@
struct audio_pcm_ops;
-typedef enum {
- AUD_OPT_INT,
- AUD_OPT_FMT,
- AUD_OPT_STR,
- AUD_OPT_BOOL
-} audio_option_tag_e;
-
-struct audio_option {
- const char *name;
- audio_option_tag_e tag;
- void *valp;
- const char *descr;
- int *overriddenp;
- int overridden;
-};
-
struct audio_callback {
void *opaque;
audio_callback_fn fn;
@@ -145,8 +129,7 @@ typedef struct audio_driver audio_driver;
struct audio_driver {
const char *name;
const char *descr;
- struct audio_option *options;
- void *(*init) (void);
+ void *(*init) (Audiodev *);
void (*fini) (void *);
struct audio_pcm_ops *pcm_ops;
int can_be_default;
@@ -193,6 +176,7 @@ struct SWVoiceCap {
typedef struct AudioState {
struct audio_driver *drv;
+ Audiodev *dev;
void *drv_opaque;
QEMUTimer *ts;
@@ -203,10 +187,13 @@ typedef struct AudioState {
int nb_hw_voices_out;
int nb_hw_voices_in;
int vm_running;
+ int64_t period_ticks;
} AudioState;
extern const struct mixeng_volume nominal_volume;
+extern const char *audio_prio_list[];
+
void audio_driver_register(audio_driver *drv);
audio_driver *audio_driver_lookup(const char *name);
@@ -248,4 +235,18 @@ static inline int audio_ring_dist (int dst, int src, int len)
#define AUDIO_STRINGIFY_(n) #n
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
+typedef struct AudiodevListEntry {
+ Audiodev *dev;
+ QSIMPLEQ_ENTRY(AudiodevListEntry) next;
+} AudiodevListEntry;
+
+typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead;
+AudiodevListHead audio_handle_legacy_opts(void);
+
+void audio_free_audiodev_list(AudiodevListHead *head);
+
+void audio_create_pdos(Audiodev *dev);
+AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev);
+AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev);
+
#endif /* QEMU_AUDIO_INT_H */
diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c
new file mode 100644
index 0000000000..6d140119d9
--- /dev/null
+++ b/audio/audio_legacy.c
@@ -0,0 +1,544 @@
+/*
+ * QEMU Audio subsystem: legacy configuration handling
+ *
+ * Copyright (c) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "audio.h"
+#include "audio_int.h"
+#include "qemu-common.h"
+#include "qemu/cutils.h"
+#include "qapi/error.h"
+#include "qapi/qapi-visit-audio.h"
+#include "qapi/visitor-impl.h"
+
+#define AUDIO_CAP "audio-legacy"
+#include "audio_int.h"
+
+static uint32_t toui32(const char *str)
+{
+ unsigned long long ret;
+ if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) {
+ dolog("Invalid integer value `%s'\n", str);
+ exit(1);
+ }
+ return ret;
+}
+
+/* helper functions to convert env variables */
+static void get_bool(const char *env, bool *dst, bool *has_dst)
+{
+ const char *val = getenv(env);
+ if (val) {
+ *dst = toui32(val) != 0;
+ *has_dst = true;
+ }
+}
+
+static void get_int(const char *env, uint32_t *dst, bool *has_dst)
+{
+ const char *val = getenv(env);
+ if (val) {
+ *dst = toui32(val);
+ *has_dst = true;
+ }
+}
+
+static void get_str(const char *env, char **dst, bool *has_dst)
+{
+ const char *val = getenv(env);
+ if (val) {
+ if (*has_dst) {
+ g_free(*dst);
+ }
+ *dst = g_strdup(val);
+ *has_dst = true;
+ }
+}
+
+static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst)
+{
+ const char *val = getenv(env);
+ if (val) {
+ size_t i;
+ for (i = 0; AudioFormat_lookup.size; ++i) {
+ if (strcasecmp(val, AudioFormat_lookup.array[i]) == 0) {
+ *dst = i;
+ *has_dst = true;
+ return;
+ }
+ }
+
+ dolog("Invalid audio format `%s'\n", val);
+ exit(1);
+ }
+}
+
+
+static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst)
+{
+ const char *val = getenv(env);
+ if (val) {
+ *dst = toui32(val) * 1000;
+ *has_dst = true;
+ }
+}
+
+static uint32_t frames_to_usecs(uint32_t frames,
+ AudiodevPerDirectionOptions *pdo)
+{
+ uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100;
+ return (frames * 1000000 + freq / 2) / freq;
+}
+
+
+static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
+ AudiodevPerDirectionOptions *pdo)
+{
+ const char *val = getenv(env);
+ if (val) {
+ *dst = frames_to_usecs(toui32(val), pdo);
+ *has_dst = true;
+ }
+}
+
+static uint32_t samples_to_usecs(uint32_t samples,
+ AudiodevPerDirectionOptions *pdo)
+{
+ uint32_t channels = pdo->has_channels ? pdo->channels : 2;
+ return frames_to_usecs(samples / channels, pdo);
+}
+
+static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
+ AudiodevPerDirectionOptions *pdo)
+{
+ const char *val = getenv(env);
+ if (val) {
+ *dst = samples_to_usecs(toui32(val), pdo);
+ *has_dst = true;
+ }
+}
+
+static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo)
+{
+ AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16;
+ uint32_t bytes_per_sample = audioformat_bytes_per_sample(fmt);
+ return samples_to_usecs(bytes / bytes_per_sample, pdo);
+}
+
+static void get_bytes_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
+ AudiodevPerDirectionOptions *pdo)
+{
+ const char *val = getenv(env);
+ if (val) {
+ *dst = bytes_to_usecs(toui32(val), pdo);
+ *has_dst = true;
+ }
+}
+
+/* backend specific functions */
+/* ALSA */
+static void handle_alsa_per_direction(
+ AudiodevAlsaPerDirectionOptions *apdo, const char *prefix)
+{
+ char buf[64];
+ size_t len = strlen(prefix);
+ bool size_in_usecs = false;
+ bool dummy;
+
+ memcpy(buf, prefix, len);
+ strcpy(buf + len, "TRY_POLL");
+ get_bool(buf, &apdo->try_poll, &apdo->has_try_poll);
+
+ strcpy(buf + len, "DEV");
+ get_str(buf, &apdo->dev, &apdo->has_dev);
+
+ strcpy(buf + len, "SIZE_IN_USEC");
+ get_bool(buf, &size_in_usecs, &dummy);
+
+ strcpy(buf + len, "PERIOD_SIZE");
+ get_int(buf, &apdo->period_length, &apdo->has_period_length);
+ if (apdo->has_period_length && !size_in_usecs) {
+ apdo->period_length = frames_to_usecs(
+ apdo->period_length,
+ qapi_AudiodevAlsaPerDirectionOptions_base(apdo));
+ }
+
+ strcpy(buf + len, "BUFFER_SIZE");
+ get_int(buf, &apdo->buffer_length, &apdo->has_buffer_length);
+ if (apdo->has_buffer_length && !size_in_usecs) {
+ apdo->buffer_length = frames_to_usecs(
+ apdo->buffer_length,
+ qapi_AudiodevAlsaPerDirectionOptions_base(apdo));
+ }
+}
+
+static void handle_alsa(Audiodev *dev)
+{
+ AudiodevAlsaOptions *aopt = &dev->u.alsa;
+ handle_alsa_per_direction(aopt->in, "QEMU_ALSA_ADC_");
+ handle_alsa_per_direction(aopt->out, "QEMU_ALSA_DAC_");
+
+ get_millis_to_usecs("QEMU_ALSA_THRESHOLD",
+ &aopt->threshold, &aopt->has_threshold);
+}
+
+/* coreaudio */
+static void handle_coreaudio(Audiodev *dev)
+{
+ get_frames_to_usecs(
+ "QEMU_COREAUDIO_BUFFER_SIZE",
+ &dev->u.coreaudio.out->buffer_length,
+ &dev->u.coreaudio.out->has_buffer_length,
+ qapi_AudiodevCoreaudioPerDirectionOptions_base(dev->u.coreaudio.out));
+ get_int("QEMU_COREAUDIO_BUFFER_COUNT",
+ &dev->u.coreaudio.out->buffer_count,
+ &dev->u.coreaudio.out->has_buffer_count);
+}
+
+/* dsound */
+static void handle_dsound(Audiodev *dev)
+{
+ get_millis_to_usecs("QEMU_DSOUND_LATENCY_MILLIS",
+ &dev->u.dsound.latency, &dev->u.dsound.has_latency);
+ get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_OUT",
+ &dev->u.dsound.out->buffer_length,
+ &dev->u.dsound.out->has_buffer_length,
+ dev->u.dsound.out);
+ get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_IN",
+ &dev->u.dsound.in->buffer_length,
+ &dev->u.dsound.in->has_buffer_length,
+ dev->u.dsound.in);
+}
+
+/* OSS */
+static void handle_oss_per_direction(
+ AudiodevOssPerDirectionOptions *opdo, const char *try_poll_env,
+ const char *dev_env)
+{
+ get_bool(try_poll_env, &opdo->try_poll, &opdo->has_try_poll);
+ get_str(dev_env, &opdo->dev, &opdo->has_dev);
+
+ get_bytes_to_usecs("QEMU_OSS_FRAGSIZE",
+ &opdo->buffer_length, &opdo->has_buffer_length,
+ qapi_AudiodevOssPerDirectionOptions_base(opdo));
+ get_int("QEMU_OSS_NFRAGS", &opdo->buffer_count,
+ &opdo->has_buffer_count);
+}
+
+static void handle_oss(Audiodev *dev)
+{
+ AudiodevOssOptions *oopt = &dev->u.oss;
+ handle_oss_per_direction(oopt->in, "QEMU_AUDIO_ADC_TRY_POLL",
+ "QEMU_OSS_ADC_DEV");
+ handle_oss_per_direction(oopt->out, "QEMU_AUDIO_DAC_TRY_POLL",
+ "QEMU_OSS_DAC_DEV");
+
+ get_bool("QEMU_OSS_MMAP", &oopt->try_mmap, &oopt->has_try_mmap);
+ get_bool("QEMU_OSS_EXCLUSIVE", &oopt->exclusive, &oopt->has_exclusive);
+ get_int("QEMU_OSS_POLICY", &oopt->dsp_policy, &oopt->has_dsp_policy);
+}
+
+/* pulseaudio */
+static void handle_pa_per_direction(
+ AudiodevPaPerDirectionOptions *ppdo, const char *env)
+{
+ get_str(env, &ppdo->name, &ppdo->has_name);
+}
+
+static void handle_pa(Audiodev *dev)
+{
+ handle_pa_per_direction(dev->u.pa.in, "QEMU_PA_SOURCE");
+ handle_pa_per_direction(dev->u.pa.out, "QEMU_PA_SINK");
+
+ get_samples_to_usecs(
+ "QEMU_PA_SAMPLES", &dev->u.pa.in->buffer_length,
+ &dev->u.pa.in->has_buffer_length,
+ qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.in));
+ get_samples_to_usecs(
+ "QEMU_PA_SAMPLES", &dev->u.pa.out->buffer_length,
+ &dev->u.pa.out->has_buffer_length,
+ qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.out));
+
+ get_str("QEMU_PA_SERVER", &dev->u.pa.server, &dev->u.pa.has_server);
+}
+
+/* SDL */
+static void handle_sdl(Audiodev *dev)
+{
+ /* SDL is output only */
+ get_samples_to_usecs("QEMU_SDL_SAMPLES", &dev->u.sdl.out->buffer_length,
+ &dev->u.sdl.out->has_buffer_length, dev->u.sdl.out);
+}
+
+/* wav */
+static void handle_wav(Audiodev *dev)
+{
+ get_int("QEMU_WAV_FREQUENCY",
+ &dev->u.wav.out->frequency, &dev->u.wav.out->has_frequency);
+ get_fmt("QEMU_WAV_FORMAT", &dev->u.wav.out->format,
+ &dev->u.wav.out->has_format);
+ get_int("QEMU_WAV_DAC_FIXED_CHANNELS",
+ &dev->u.wav.out->channels, &dev->u.wav.out->has_channels);
+ get_str("QEMU_WAV_PATH", &dev->u.wav.path, &dev->u.wav.has_path);
+}
+
+/* general */
+static void handle_per_direction(
+ AudiodevPerDirectionOptions *pdo, const char *prefix)
+{
+ char buf[64];
+ size_t len = strlen(prefix);
+
+ memcpy(buf, prefix, len);
+ strcpy(buf + len, "FIXED_SETTINGS");
+ get_bool(buf, &pdo->fixed_settings, &pdo->has_fixed_settings);
+
+ strcpy(buf + len, "FIXED_FREQ");
+ get_int(buf, &pdo->frequency, &pdo->has_frequency);
+
+ strcpy(buf + len, "FIXED_FMT");
+ get_fmt(buf, &pdo->format, &pdo->has_format);
+
+ strcpy(buf + len, "FIXED_CHANNELS");
+ get_int(buf, &pdo->channels, &pdo->has_channels);
+
+ strcpy(buf + len, "VOICES");
+ get_int(buf, &pdo->voices, &pdo->has_voices);
+}
+
+static AudiodevListEntry *legacy_opt(const char *drvname)
+{
+ AudiodevListEntry *e = g_malloc0(sizeof(AudiodevListEntry));
+ e->dev = g_malloc0(sizeof(Audiodev));
+ e->dev->id = g_strdup(drvname);
+ e->dev->driver = qapi_enum_parse(
+ &AudiodevDriver_lookup, drvname, -1, &error_abort);
+
+ audio_create_pdos(e->dev);
+
+ handle_per_direction(audio_get_pdo_in(e->dev), "QEMU_AUDIO_ADC_");
+ handle_per_direction(audio_get_pdo_out(e->dev), "QEMU_AUDIO_DAC_");
+
+ get_int("QEMU_AUDIO_TIMER_PERIOD",
+ &e->dev->timer_period, &e->dev->has_timer_period);
+
+ switch (e->dev->driver) {
+ case AUDIODEV_DRIVER_ALSA:
+ handle_alsa(e->dev);
+ break;
+
+ case AUDIODEV_DRIVER_COREAUDIO:
+ handle_coreaudio(e->dev);
+ break;
+
+ case AUDIODEV_DRIVER_DSOUND:
+ handle_dsound(e->dev);
+ break;
+
+ case AUDIODEV_DRIVER_OSS:
+ handle_oss(e->dev);
+ break;
+
+ case AUDIODEV_DRIVER_PA:
+ handle_pa(e->dev);
+ break;
+
+ case AUDIODEV_DRIVER_SDL:
+ handle_sdl(e->dev);
+ break;
+
+ case AUDIODEV_DRIVER_WAV:
+ handle_wav(e->dev);
+ break;
+
+ default:
+ break;
+ }
+
+ return e;
+}
+
+AudiodevListHead audio_handle_legacy_opts(void)
+{
+ const char *drvname = getenv("QEMU_AUDIO_DRV");
+ AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head);
+
+ if (drvname) {
+ AudiodevListEntry *e;
+ audio_driver *driver = audio_driver_lookup(drvname);
+ if (!driver) {
+ dolog("Unknown audio driver `%s'\n", drvname);
+ exit(1);
+ }
+ e = legacy_opt(drvname);
+ QSIMPLEQ_INSERT_TAIL(&head, e, next);
+ } else {
+ for (int i = 0; audio_prio_list[i]; i++) {
+ audio_driver *driver = audio_driver_lookup(audio_prio_list[i]);
+ if (driver && driver->can_be_default) {
+ AudiodevListEntry *e = legacy_opt(driver->name);
+ QSIMPLEQ_INSERT_TAIL(&head, e, next);
+ }
+ }
+ if (QSIMPLEQ_EMPTY(&head)) {
+ dolog("Internal error: no default audio driver available\n");
+ exit(1);
+ }
+ }
+
+ return head;
+}
+
+/* visitor to print -audiodev option */
+typedef struct {
+ Visitor visitor;
+
+ bool comma;
+ GList *path;
+} LegacyPrintVisitor;
+
+static void lv_start_struct(Visitor *v, const char *name, void **obj,
+ size_t size, Error **errp)
+{
+ LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
+ lv->path = g_list_append(lv->path, g_strdup(name));
+}
+
+static void lv_end_struct(Visitor *v, void **obj)
+{
+ LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
+ lv->path = g_list_delete_link(lv->path, g_list_last(lv->path));
+}
+
+static void lv_print_key(Visitor *v, const char *name)
+{
+ GList *e;
+ LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
+ if (lv->comma) {
+ putchar(',');
+ } else {
+ lv->comma = true;
+ }
+
+ for (e = lv->path; e; e = e->next) {
+ if (e->data) {
+ printf("%s.", (const char *) e->data);
+ }
+ }
+
+ printf("%s=", name);
+}
+
+static void lv_type_int64(Visitor *v, const char *name, int64_t *obj,
+ Error **errp)
+{
+ lv_print_key(v, name);
+ printf("%" PRIi64, *obj);
+}
+
+static void lv_type_uint64(Visitor *v, const char *name, uint64_t *obj,
+ Error **errp)
+{
+ lv_print_key(v, name);
+ printf("%" PRIu64, *obj);
+}
+
+static void lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
+{
+ lv_print_key(v, name);
+ printf("%s", *obj ? "on" : "off");
+}
+
+static void lv_type_str(Visitor *v, const char *name, char **obj, Error **errp)
+{
+ const char *str = *obj;
+ lv_print_key(v, name);
+
+ while (*str) {
+ if (*str == ',') {
+ putchar(',');
+ }
+ putchar(*str++);
+ }
+}
+
+static void lv_complete(Visitor *v, void *opaque)
+{
+ LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
+ assert(lv->path == NULL);
+}
+
+static void lv_free(Visitor *v)
+{
+ LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
+
+ g_list_free_full(lv->path, g_free);
+ g_free(lv);
+}
+
+static Visitor *legacy_visitor_new(void)
+{
+ LegacyPrintVisitor *lv = g_malloc0(sizeof(LegacyPrintVisitor));
+
+ lv->visitor.start_struct = lv_start_struct;
+ lv->visitor.end_struct = lv_end_struct;
+ /* lists not supported */
+ lv->visitor.type_int64 = lv_type_int64;
+ lv->visitor.type_uint64 = lv_type_uint64;
+ lv->visitor.type_bool = lv_type_bool;
+ lv->visitor.type_str = lv_type_str;
+
+ lv->visitor.type = VISITOR_OUTPUT;
+ lv->visitor.complete = lv_complete;
+ lv->visitor.free = lv_free;
+
+ return &lv->visitor;
+}
+
+void audio_legacy_help(void)
+{
+ AudiodevListHead head;
+ AudiodevListEntry *e;
+
+ printf("Environment variable based configuration deprecated.\n");
+ printf("Please use the new -audiodev option.\n");
+
+ head = audio_handle_legacy_opts();
+ printf("\nEquivalent -audiodev to your current environment variables:\n");
+ if (!getenv("QEMU_AUDIO_DRV")) {
+ printf("(Since you didn't specify QEMU_AUDIO_DRV, I'll list all "
+ "possibilities)\n");
+ }
+
+ QSIMPLEQ_FOREACH(e, &head, next) {
+ Visitor *v;
+ Audiodev *dev = e->dev;
+ printf("-audiodev ");
+
+ v = legacy_visitor_new();
+ visit_type_Audiodev(v, NULL, &dev, &error_abort);
+ visit_free(v);
+
+ printf("\n");
+ }
+ audio_free_audiodev_list(&head);
+}
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 7de227d2d1..1232bb54db 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -299,11 +299,42 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
return NULL;
}
+AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
+{
+ switch (dev->driver) {
+ case AUDIODEV_DRIVER_NONE:
+ return dev->u.none.TYPE;
+ case AUDIODEV_DRIVER_ALSA:
+ return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE);
+ case AUDIODEV_DRIVER_COREAUDIO:
+ return qapi_AudiodevCoreaudioPerDirectionOptions_base(
+ dev->u.coreaudio.TYPE);
+ case AUDIODEV_DRIVER_DSOUND:
+ return dev->u.dsound.TYPE;
+ case AUDIODEV_DRIVER_OSS:
+ return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE);
+ case AUDIODEV_DRIVER_PA:
+ return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
+ case AUDIODEV_DRIVER_SDL:
+ return dev->u.sdl.TYPE;
+ case AUDIODEV_DRIVER_SPICE:
+ return dev->u.spice.TYPE;
+ case AUDIODEV_DRIVER_WAV:
+ return dev->u.wav.TYPE;
+
+ case AUDIODEV_DRIVER__MAX:
+ break;
+ }
+ abort();
+}
+
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
{
HW *hw;
+ AudioState *s = &glob_audio_state;
+ AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
- if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
+ if (pdo->fixed_settings) {
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
if (hw) {
return hw;
@@ -331,9 +362,11 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
SW *sw;
HW *hw;
struct audsettings hw_as;
+ AudioState *s = &glob_audio_state;
+ AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
- if (glue (conf.fixed_, TYPE).enabled) {
- hw_as = glue (conf.fixed_, TYPE).settings;
+ if (pdo->fixed_settings) {
+ hw_as = audiodev_to_audsettings(pdo);
}
else {
hw_as = *as;
@@ -398,6 +431,7 @@ SW *glue (AUD_open_, TYPE) (
)
{
AudioState *s = &glob_audio_state;
+ AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
@@ -422,7 +456,7 @@ SW *glue (AUD_open_, TYPE) (
return sw;
}
- if (!glue (conf.fixed_, TYPE).enabled && sw) {
+ if (!pdo->fixed_settings && sw) {
glue (AUD_close_, TYPE) (card, sw);
sw = NULL;
}
diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c
index 6900008d0c..b938fd667b 100644
--- a/audio/audio_win_int.c
+++ b/audio/audio_win_int.c
@@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
wfx->cbSize = 0;
switch (as->fmt) {
- case AUD_FMT_S8:
- case AUD_FMT_U8:
+ case AUDIO_FORMAT_S8:
+ case AUDIO_FORMAT_U8:
wfx->wBitsPerSample = 8;
break;
- case AUD_FMT_S16:
- case AUD_FMT_U16:
+ case AUDIO_FORMAT_S16:
+ case AUDIO_FORMAT_U16:
wfx->wBitsPerSample = 16;
wfx->nAvgBytesPerSec <<= 1;
wfx->nBlockAlign <<= 1;
break;
- case AUD_FMT_S32:
- case AUD_FMT_U32:
+ case AUDIO_FORMAT_S32:
+ case AUDIO_FORMAT_U32:
wfx->wBitsPerSample = 32;
wfx->nAvgBytesPerSec <<= 2;
wfx->nBlockAlign <<= 2;
@@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
switch (wfx->wBitsPerSample) {
case 8:
- as->fmt = AUD_FMT_U8;
+ as->fmt = AUDIO_FORMAT_U8;
break;
case 16:
- as->fmt = AUD_FMT_S16;
+ as->fmt = AUDIO_FORMAT_S16;
break;
case 32:
- as->fmt = AUD_FMT_S32;
+ as->fmt = AUDIO_FORMAT_S32;
break;
default:
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index 638c60b300..1ee43b7d5f 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -36,11 +36,6 @@
#define MAC_OS_X_VERSION_10_6 1060
#endif
-typedef struct {
- int buffer_frames;
- int nbuffers;
-} CoreaudioConf;
-
typedef struct coreaudioVoiceOut {
HWVoiceOut hw;
pthread_mutex_t mutex;
@@ -507,7 +502,9 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
int err;
const char *typ = "playback";
AudioValueRange frameRange;
- CoreaudioConf *conf = drv_opaque;
+ Audiodev *dev = drv_opaque;
+ AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
+ int frames;
/* create mutex */
err = pthread_mutex_init(&core->mutex, NULL);
@@ -538,16 +535,17 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
return -1;
}
- if (frameRange.mMinimum > conf->buffer_frames) {
+ frames = audio_buffer_frames(
+ qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
+ if (frameRange.mMinimum > 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 < frames) {
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
}
else {
- core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
+ core->audioDevicePropertyBufferFrameSize = frames;
}
/* set Buffer Frame Size */
@@ -568,7 +566,8 @@ 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 = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) *
+ core->audioDevicePropertyBufferFrameSize;
/* get StreamFormat */
status = coreaudio_get_streamformat(core->outputDeviceID,
@@ -680,40 +679,15 @@ 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)
+static void *coreaudio_audio_init(Audiodev *dev)
{
- CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
- *conf = glob_conf;
-
- return conf;
+ return dev;
}
static void coreaudio_audio_fini (void *opaque)
{
- g_free(opaque);
}
-static struct audio_option coreaudio_options[] = {
- {
- .name = "BUFFER_SIZE",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.buffer_frames,
- .descr = "Size of the buffer in frames"
- },
- {
- .name = "BUFFER_COUNT",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.nbuffers,
- .descr = "Number of buffers"
- },
- { /* End of list */ }
-};
-
static struct audio_pcm_ops coreaudio_pcm_ops = {
.init_out = coreaudio_init_out,
.fini_out = coreaudio_fini_out,
@@ -725,7 +699,6 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
static struct audio_driver coreaudio_audio_driver = {
.name = "coreaudio",
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
- .options = coreaudio_options,
.init = coreaudio_audio_init,
.fini = coreaudio_audio_fini,
.pcm_ops = &coreaudio_pcm_ops,
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
index b439f33f58..8ece870c9e 100644
--- a/audio/dsound_template.h
+++ b/audio/dsound_template.h
@@ -167,17 +167,18 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
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;
DSCBUFFERDESC bd;
DSCBCAPS bc;
+ AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.in;
#else
const char *typ = "DAC";
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
DSBUFFERDESC bd;
DSBCAPS bc;
+ AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.out;
#endif
if (!s->FIELD2) {
@@ -193,8 +194,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
memset (&bd, 0, sizeof (bd));
bd.dwSize = sizeof (bd);
bd.lpwfxFormat = &wfx;
+ bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880);
#ifdef DSBTYPE_IN
- bd.dwBufferBytes = conf->bufsize_in;
hr = IDirectSoundCapture_CreateCaptureBuffer (
s->dsound_capture,
&bd,
@@ -203,7 +204,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
);
#else
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
- bd.dwBufferBytes = conf->bufsize_out;
hr = IDirectSound_CreateSoundBuffer (
s->dsound,
&bd,
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index 3ed73a30d1..a7d04b5033 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -32,6 +32,7 @@
#define AUDIO_CAP "dsound"
#include "audio_int.h"
+#include "qemu/host-utils.h"
#include <windows.h>
#include <mmsystem.h>
@@ -43,16 +44,10 @@
/* #define DEBUG_DSOUND */
typedef struct {
- int bufsize_in;
- int bufsize_out;
- int latency_millis;
-} DSoundConf;
-
-typedef struct {
LPDIRECTSOUND dsound;
LPDIRECTSOUNDCAPTURE dsound_capture;
struct audsettings settings;
- DSoundConf conf;
+ Audiodev *dev;
} dsound;
typedef struct {
@@ -248,9 +243,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
dsound_log_hresult (hr);
}
-static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
+static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
{
- return (millis * info->bytes_per_second) / 1000;
+ return muldiv64(usecs, info->bytes_per_second, 1000000);
}
#ifdef DEBUG_DSOUND
@@ -478,7 +473,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
LPVOID p1, p2;
int bufsize;
dsound *s = ds->s;
- DSoundConf *conf = &s->conf;
+ AudiodevDsoundOptions *dso = &s->dev->u.dsound;
if (!dsb) {
dolog ("Attempt to run empty with playback buffer\n");
@@ -501,14 +496,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
len = live << hwshift;
if (ds->first_time) {
- if (conf->latency_millis) {
+ if (dso->latency) {
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;
+ usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
old_pos %= bufsize;
old_pos &= ~hw->info.align;
}
@@ -747,12 +742,6 @@ 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;
@@ -783,13 +772,22 @@ static void dsound_audio_fini (void *opaque)
g_free(s);
}
-static void *dsound_audio_init (void)
+static void *dsound_audio_init(Audiodev *dev)
{
int err;
HRESULT hr;
dsound *s = g_malloc0(sizeof(dsound));
+ AudiodevDsoundOptions *dso;
+
+ assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
+ s->dev = dev;
+ dso = &dev->u.dsound;
+
+ if (!dso->has_latency) {
+ dso->has_latency = true;
+ dso->latency = 10000; /* 10 ms */
+ }
- s->conf = glob_conf;
hr = CoInitialize (NULL);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize COM\n");
@@ -854,28 +852,6 @@ static void *dsound_audio_init (void)
return s;
}
-static struct audio_option dsound_options[] = {
- {
- .name = "LATENCY_MILLIS",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.latency_millis,
- .descr = "(undocumented)"
- },
- {
- .name = "BUFSIZE_OUT",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.bufsize_out,
- .descr = "(undocumented)"
- },
- {
- .name = "BUFSIZE_IN",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.bufsize_in,
- .descr = "(undocumented)"
- },
- { /* End of list */ }
-};
-
static struct audio_pcm_ops dsound_pcm_ops = {
.init_out = dsound_init_out,
.fini_out = dsound_fini_out,
@@ -893,7 +869,6 @@ static struct audio_pcm_ops dsound_pcm_ops = {
static struct audio_driver dsound_audio_driver = {
.name = "dsound",
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
- .options = dsound_options,
.init = dsound_audio_init,
.fini = dsound_audio_fini,
.pcm_ops = &dsound_pcm_ops,
diff --git a/audio/noaudio.c b/audio/noaudio.c
index 1bfebeca7d..ccc611fc84 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -136,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
return 0;
}
-static void *no_audio_init (void)
+static void *no_audio_init(Audiodev *dev)
{
return &no_audio_init;
}
@@ -163,7 +163,6 @@ static struct audio_pcm_ops no_pcm_ops = {
static struct audio_driver no_audio_driver = {
.name = "none",
.descr = "Timer based audio emulation",
- .options = NULL,
.init = no_audio_init,
.fini = no_audio_fini,
.pcm_ops = &no_pcm_ops,
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 6c69622b4c..fc28981a39 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -37,16 +37,6 @@
#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;
@@ -56,7 +46,7 @@ typedef struct OSSVoiceOut {
int fragsize;
int mmapped;
int pending;
- OSSConf *conf;
+ Audiodev *dev;
} OSSVoiceOut;
typedef struct OSSVoiceIn {
@@ -65,12 +55,12 @@ typedef struct OSSVoiceIn {
int fd;
int nfrags;
int fragsize;
- OSSConf *conf;
+ Audiodev *dev;
} OSSVoiceIn;
struct oss_params {
int freq;
- audfmt_e fmt;
+ int fmt;
int nchannels;
int nfrags;
int fragsize;
@@ -148,16 +138,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
-static int aud_to_ossfmt (audfmt_e fmt, int endianness)
+static int aud_to_ossfmt (AudioFormat fmt, int endianness)
{
switch (fmt) {
- case AUD_FMT_S8:
+ case AUDIO_FORMAT_S8:
return AFMT_S8;
- case AUD_FMT_U8:
+ case AUDIO_FORMAT_U8:
return AFMT_U8;
- case AUD_FMT_S16:
+ case AUDIO_FORMAT_S16:
if (endianness) {
return AFMT_S16_BE;
}
@@ -165,7 +155,7 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness)
return AFMT_S16_LE;
}
- case AUD_FMT_U16:
+ case AUDIO_FORMAT_U16:
if (endianness) {
return AFMT_U16_BE;
}
@@ -182,37 +172,37 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness)
}
}
-static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
+static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness)
{
switch (ossfmt) {
case AFMT_S8:
*endianness = 0;
- *fmt = AUD_FMT_S8;
+ *fmt = AUDIO_FORMAT_S8;
break;
case AFMT_U8:
*endianness = 0;
- *fmt = AUD_FMT_U8;
+ *fmt = AUDIO_FORMAT_U8;
break;
case AFMT_S16_LE:
*endianness = 0;
- *fmt = AUD_FMT_S16;
+ *fmt = AUDIO_FORMAT_S16;
break;
case AFMT_U16_LE:
*endianness = 0;
- *fmt = AUD_FMT_U16;
+ *fmt = AUDIO_FORMAT_U16;
break;
case AFMT_S16_BE:
*endianness = 1;
- *fmt = AUD_FMT_S16;
+ *fmt = AUDIO_FORMAT_S16;
break;
case AFMT_U16_BE:
*endianness = 1;
- *fmt = AUD_FMT_U16;
+ *fmt = AUDIO_FORMAT_U16;
break;
default:
@@ -262,19 +252,25 @@ 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, OSSConf* conf)
+static int oss_open(int in, struct oss_params *req, audsettings *as,
+ struct oss_params *obt, int *pfd, Audiodev *dev)
{
+ AudiodevOssOptions *oopts = &dev->u.oss;
+ AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out;
int fd;
- int oflags = conf->exclusive ? O_EXCL : 0;
+ int oflags = (oopts->has_exclusive && oopts->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 = opdo->has_dev ? opdo->dev : "/dev/dsp";
const char *typ = in ? "ADC" : "DAC";
+#ifdef USE_DSP_POLICY
+ int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
+#endif
/* Kludge needed to have working mmap on Linux */
- oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
+ oflags |= (oopts->has_try_mmap && oopts->try_mmap) ?
+ O_RDWR : (in ? O_RDONLY : O_WRONLY);
fd = open (dspname, oflags | O_NONBLOCK);
if (-1 == fd) {
@@ -285,6 +281,9 @@ static int oss_open (int in, struct oss_params *req,
freq = req->freq;
nchannels = req->nchannels;
fmt = req->fmt;
+ req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4;
+ req->fragsize = audio_buffer_bytes(
+ qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220);
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
@@ -308,18 +307,18 @@ static int oss_open (int in, struct oss_params *req,
}
#ifdef USE_DSP_POLICY
- if (conf->policy >= 0) {
+ if (policy >= 0) {
int version;
if (!oss_get_version (fd, &version, typ)) {
trace_oss_version(version);
if (version >= 0x040000) {
- int policy = conf->policy;
- if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
+ int policy2 = policy;
+ if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) {
oss_logerr2 (errno, typ,
"Failed to set timing policy to %d\n",
- conf->policy);
+ policy);
goto err;
}
setfragment = 0;
@@ -500,19 +499,18 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
int endianness;
int err;
int fd;
- audfmt_e effective_fmt;
+ AudioFormat effective_fmt;
struct audsettings obt_as;
- OSSConf *conf = drv_opaque;
+ Audiodev *dev = drv_opaque;
+ AudiodevOssOptions *oopts = &dev->u.oss;
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 (0, &req, &obt, &fd, conf)) {
+ if (oss_open(0, &req, as, &obt, &fd, dev)) {
return -1;
}
@@ -539,7 +537,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 (oopts->has_try_mmap && oopts->try_mmap) {
oss->pcm_buf = mmap (
NULL,
hw->samples << hw->info.shift,
@@ -597,7 +595,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
}
oss->fd = fd;
- oss->conf = conf;
+ oss->dev = dev;
return 0;
}
@@ -605,16 +603,12 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
int trig;
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+ AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
switch (cmd) {
case VOICE_ENABLE:
{
- va_list ap;
- int poll_mode;
-
- va_start (ap, cmd);
- poll_mode = va_arg (ap, int);
- va_end (ap);
+ bool poll_mode = opdo->try_poll;
ldebug ("enabling voice\n");
if (poll_mode) {
@@ -667,18 +661,16 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
int endianness;
int err;
int fd;
- audfmt_e effective_fmt;
+ AudioFormat effective_fmt;
struct audsettings obt_as;
- OSSConf *conf = drv_opaque;
+ Audiodev *dev = 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, conf)) {
+ if (oss_open(1, &req, as, &obt, &fd, dev)) {
return -1;
}
@@ -712,7 +704,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}
oss->fd = fd;
- oss->conf = conf;
+ oss->dev = dev;
return 0;
}
@@ -803,16 +795,12 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size)
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+ AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
switch (cmd) {
case VOICE_ENABLE:
{
- va_list ap;
- int poll_mode;
-
- va_start (ap, cmd);
- poll_mode = va_arg (ap, int);
- va_end (ap);
+ bool poll_mode = opdo->try_poll;
if (poll_mode) {
oss_poll_in (hw);
@@ -832,82 +820,36 @@ 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_init_per_direction(AudiodevOssPerDirectionOptions *opdo)
+{
+ if (!opdo->has_try_poll) {
+ opdo->try_poll = true;
+ opdo->has_try_poll = true;
+ }
+}
-static void *oss_audio_init (void)
+static void *oss_audio_init(Audiodev *dev)
{
- OSSConf *conf = g_malloc(sizeof(OSSConf));
- *conf = glob_conf;
+ AudiodevOssOptions *oopts;
+ assert(dev->driver == AUDIODEV_DRIVER_OSS);
+
+ oopts = &dev->u.oss;
+ oss_init_per_direction(oopts->in);
+ oss_init_per_direction(oopts->out);
- if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
- access(conf->devpath_out, R_OK | W_OK) < 0) {
- g_free(conf);
+ if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp",
+ R_OK | W_OK) < 0 ||
+ access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp",
+ R_OK | W_OK) < 0) {
return NULL;
}
- return conf;
+ return dev;
}
static void oss_audio_fini (void *opaque)
{
- g_free(opaque);
}
-static struct audio_option oss_options[] = {
- {
- .name = "FRAGSIZE",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.fragsize,
- .descr = "Fragment size in bytes"
- },
- {
- .name = "NFRAGS",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.nfrags,
- .descr = "Number of fragments"
- },
- {
- .name = "MMAP",
- .tag = AUD_OPT_BOOL,
- .valp = &glob_conf.try_mmap,
- .descr = "Try using memory mapped access"
- },
- {
- .name = "DAC_DEV",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.devpath_out,
- .descr = "Path to DAC device"
- },
- {
- .name = "ADC_DEV",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.devpath_in,
- .descr = "Path to ADC device"
- },
- {
- .name = "EXCLUSIVE",
- .tag = AUD_OPT_BOOL,
- .valp = &glob_conf.exclusive,
- .descr = "Open device in exclusive mode (vmix won't work)"
- },
-#ifdef USE_DSP_POLICY
- {
- .name = "POLICY",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.policy,
- .descr = "Set the timing policy of the device, -1 to use fragment mode",
- },
-#endif
- { /* End of list */ }
-};
-
static struct audio_pcm_ops oss_pcm_ops = {
.init_out = oss_init_out,
.fini_out = oss_fini_out,
@@ -925,7 +867,6 @@ static struct audio_pcm_ops oss_pcm_ops = {
static struct audio_driver oss_audio_driver = {
.name = "oss",
.descr = "OSS http://www.opensound.com",
- .options = oss_options,
.init = oss_audio_init,
.fini = oss_audio_fini,
.pcm_ops = &oss_pcm_ops,
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 6153b908da..5d410ed73f 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -2,6 +2,7 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "audio.h"
+#include "qapi/opts-visitor.h"
#include <pulse/pulseaudio.h>
@@ -10,14 +11,7 @@
#include "audio_pt_int.h"
typedef struct {
- int samples;
- char *server;
- char *sink;
- char *source;
-} PAConf;
-
-typedef struct {
- PAConf conf;
+ Audiodev *dev;
pa_threaded_mainloop *mainloop;
pa_context *context;
} paaudio;
@@ -32,6 +26,7 @@ typedef struct {
void *pcm_buf;
struct audio_pt pt;
paaudio *g;
+ int samples;
} PAVoiceOut;
typedef struct {
@@ -46,6 +41,7 @@ typedef struct {
const void *read_data;
size_t read_index, read_length;
paaudio *g;
+ int samples;
} PAVoiceIn;
static void qpa_audio_fini(void *opaque);
@@ -227,7 +223,7 @@ static void *qpa_thread_out (void *arg)
}
}
- decr = to_mix = audio_MIN(pa->live, pa->g->conf.samples >> 5);
+ decr = to_mix = audio_MIN(pa->live, pa->samples >> 5);
rpos = pa->rpos;
if (audio_pt_unlock(&pa->pt, __func__)) {
@@ -319,7 +315,7 @@ static void *qpa_thread_in (void *arg)
}
}
- incr = to_grab = audio_MIN(pa->dead, pa->g->conf.samples >> 5);
+ incr = to_grab = audio_MIN(pa->dead, pa->samples >> 5);
wpos = pa->wpos;
if (audio_pt_unlock(&pa->pt, __func__)) {
@@ -385,21 +381,21 @@ static int qpa_read (SWVoiceIn *sw, void *buf, int len)
return audio_pcm_sw_read (sw, buf, len);
}
-static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
+static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
{
int format;
switch (afmt) {
- case AUD_FMT_S8:
- case AUD_FMT_U8:
+ case AUDIO_FORMAT_S8:
+ case AUDIO_FORMAT_U8:
format = PA_SAMPLE_U8;
break;
- case AUD_FMT_S16:
- case AUD_FMT_U16:
+ case AUDIO_FORMAT_S16:
+ case AUDIO_FORMAT_U16:
format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
break;
- case AUD_FMT_S32:
- case AUD_FMT_U32:
+ case AUDIO_FORMAT_S32:
+ case AUDIO_FORMAT_U32:
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
break;
default:
@@ -410,26 +406,26 @@ static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
return format;
}
-static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
+static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
{
switch (fmt) {
case PA_SAMPLE_U8:
- return AUD_FMT_U8;
+ return AUDIO_FORMAT_U8;
case PA_SAMPLE_S16BE:
*endianness = 1;
- return AUD_FMT_S16;
+ return AUDIO_FORMAT_S16;
case PA_SAMPLE_S16LE:
*endianness = 0;
- return AUD_FMT_S16;
+ return AUDIO_FORMAT_S16;
case PA_SAMPLE_S32BE:
*endianness = 1;
- return AUD_FMT_S32;
+ return AUDIO_FORMAT_S32;
case PA_SAMPLE_S32LE:
*endianness = 0;
- return AUD_FMT_S32;
+ return AUDIO_FORMAT_S32;
default:
dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
- return AUD_FMT_U8;
+ return AUDIO_FORMAT_U8;
}
}
@@ -546,6 +542,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
struct audsettings obt_as = *as;
PAVoiceOut *pa = (PAVoiceOut *) hw;
paaudio *g = pa->g = drv_opaque;
+ AudiodevPaOptions *popts = &g->dev->u.pa;
+ AudiodevPaPerDirectionOptions *ppdo = popts->out;
ss.format = audfmt_to_pa (as->fmt, as->endianness);
ss.channels = as->nchannels;
@@ -566,7 +564,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
g,
"qemu",
PA_STREAM_PLAYBACK,
- g->conf.sink,
+ ppdo->has_name ? ppdo->name : NULL,
&ss,
NULL, /* channel map */
&ba, /* buffering attributes */
@@ -578,7 +576,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
}
audio_pcm_init_info (&hw->info, &obt_as);
- hw->samples = g->conf.samples;
+ hw->samples = pa->samples = audio_buffer_samples(
+ qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
pa->rpos = hw->rpos;
if (!pa->pcm_buf) {
@@ -612,6 +611,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
struct audsettings obt_as = *as;
PAVoiceIn *pa = (PAVoiceIn *) hw;
paaudio *g = pa->g = drv_opaque;
+ AudiodevPaOptions *popts = &g->dev->u.pa;
+ AudiodevPaPerDirectionOptions *ppdo = popts->in;
ss.format = audfmt_to_pa (as->fmt, as->endianness);
ss.channels = as->nchannels;
@@ -623,7 +624,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
g,
"qemu",
PA_STREAM_RECORD,
- g->conf.source,
+ ppdo->has_name ? ppdo->name : NULL,
&ss,
NULL, /* channel map */
NULL, /* buffering attributes */
@@ -635,7 +636,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}
audio_pcm_init_info (&hw->info, &obt_as);
- hw->samples = g->conf.samples;
+ hw->samples = pa->samples = audio_buffer_samples(
+ qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
pa->wpos = hw->wpos;
if (!pa->pcm_buf) {
@@ -808,13 +810,13 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
}
/* common */
-static PAConf glob_conf = {
- .samples = 4096,
-};
-
-static void *qpa_audio_init (void)
+static void *qpa_audio_init(Audiodev *dev)
{
- if (glob_conf.server == NULL) {
+ paaudio *g;
+ AudiodevPaOptions *popts = &dev->u.pa;
+ const char *server;
+
+ if (!popts->has_server) {
char pidfile[64];
char *runtime;
struct stat st;
@@ -829,8 +831,12 @@ static void *qpa_audio_init (void)
}
}
- paaudio *g = g_malloc(sizeof(paaudio));
- g->conf = glob_conf;
+ assert(dev->driver == AUDIODEV_DRIVER_PA);
+
+ g = g_malloc(sizeof(paaudio));
+ server = popts->has_server ? popts->server : NULL;
+
+ g->dev = dev;
g->mainloop = NULL;
g->context = NULL;
@@ -840,14 +846,14 @@ static void *qpa_audio_init (void)
}
g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
- g->conf.server);
+ server);
if (!g->context) {
goto fail;
}
pa_context_set_state_callback (g->context, context_state_cb, g);
- if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) {
+ if (pa_context_connect(g->context, server, 0, NULL) < 0) {
qpa_logerr (pa_context_errno (g->context),
"pa_context_connect() failed\n");
goto fail;
@@ -910,34 +916,6 @@ static void qpa_audio_fini (void *opaque)
g_free(g);
}
-struct audio_option qpa_options[] = {
- {
- .name = "SAMPLES",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.samples,
- .descr = "buffer size in samples"
- },
- {
- .name = "SERVER",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.server,
- .descr = "server address"
- },
- {
- .name = "SINK",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.sink,
- .descr = "sink device name"
- },
- {
- .name = "SOURCE",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.source,
- .descr = "source device name"
- },
- { /* End of list */ }
-};
-
static struct audio_pcm_ops qpa_pcm_ops = {
.init_out = qpa_init_out,
.fini_out = qpa_fini_out,
@@ -955,7 +933,6 @@ static struct audio_pcm_ops qpa_pcm_ops = {
static struct audio_driver pa_audio_driver = {
.name = "pa",
.descr = "http://www.pulseaudio.org/",
- .options = qpa_options,
.init = qpa_audio_init,
.fini = qpa_audio_fini,
.pcm_ops = &qpa_pcm_ops,
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index f7ee70b153..ff9248ba68 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -44,16 +44,11 @@ typedef struct SDLVoiceOut {
int decr;
} SDLVoiceOut;
-static struct {
- int nb_samples;
-} conf = {
- .nb_samples = 1024
-};
-
static struct SDLAudioState {
int exit;
int initialized;
bool driver_created;
+ Audiodev *dev;
} glob_sdl;
typedef struct SDLAudioState SDLAudioState;
@@ -68,19 +63,19 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
}
-static int aud_to_sdlfmt (audfmt_e fmt)
+static int aud_to_sdlfmt (AudioFormat fmt)
{
switch (fmt) {
- case AUD_FMT_S8:
+ case AUDIO_FORMAT_S8:
return AUDIO_S8;
- case AUD_FMT_U8:
+ case AUDIO_FORMAT_U8:
return AUDIO_U8;
- case AUD_FMT_S16:
+ case AUDIO_FORMAT_S16:
return AUDIO_S16LSB;
- case AUD_FMT_U16:
+ case AUDIO_FORMAT_U16:
return AUDIO_U16LSB;
default:
@@ -92,37 +87,37 @@ static int aud_to_sdlfmt (audfmt_e fmt)
}
}
-static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness)
+static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness)
{
switch (sdlfmt) {
case AUDIO_S8:
*endianness = 0;
- *fmt = AUD_FMT_S8;
+ *fmt = AUDIO_FORMAT_S8;
break;
case AUDIO_U8:
*endianness = 0;
- *fmt = AUD_FMT_U8;
+ *fmt = AUDIO_FORMAT_U8;
break;
case AUDIO_S16LSB:
*endianness = 0;
- *fmt = AUD_FMT_S16;
+ *fmt = AUDIO_FORMAT_S16;
break;
case AUDIO_U16LSB:
*endianness = 0;
- *fmt = AUD_FMT_U16;
+ *fmt = AUDIO_FORMAT_U16;
break;
case AUDIO_S16MSB:
*endianness = 1;
- *fmt = AUD_FMT_S16;
+ *fmt = AUDIO_FORMAT_S16;
break;
case AUDIO_U16MSB:
*endianness = 1;
- *fmt = AUD_FMT_U16;
+ *fmt = AUDIO_FORMAT_U16;
break;
default:
@@ -265,13 +260,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
SDL_AudioSpec req, obt;
int endianness;
int err;
- audfmt_e effective_fmt;
+ AudioFormat effective_fmt;
struct audsettings obt_as;
req.freq = as->freq;
req.format = aud_to_sdlfmt (as->fmt);
req.channels = as->nchannels;
- req.samples = conf.nb_samples;
+ req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610);
req.callback = sdl_callback;
req.userdata = sdl;
@@ -315,7 +310,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0;
}
-static void *sdl_audio_init (void)
+static void *sdl_audio_init(Audiodev *dev)
{
SDLAudioState *s = &glob_sdl;
if (s->driver_created) {
@@ -329,6 +324,7 @@ static void *sdl_audio_init (void)
}
s->driver_created = true;
+ s->dev = dev;
return s;
}
@@ -338,18 +334,9 @@ static void sdl_audio_fini (void *opaque)
sdl_close (s);
SDL_QuitSubSystem (SDL_INIT_AUDIO);
s->driver_created = false;
+ s->dev = NULL;
}
-static struct audio_option sdl_options[] = {
- {
- .name = "SAMPLES",
- .tag = AUD_OPT_INT,
- .valp = &conf.nb_samples,
- .descr = "Size of SDL buffer in samples"
- },
- { /* End of list */ }
-};
-
static struct audio_pcm_ops sdl_pcm_ops = {
.init_out = sdl_init_out,
.fini_out = sdl_fini_out,
@@ -361,7 +348,6 @@ static struct audio_pcm_ops sdl_pcm_ops = {
static struct audio_driver sdl_audio_driver = {
.name = "sdl",
.descr = "SDL http://www.libsdl.org",
- .options = sdl_options,
.init = sdl_audio_init,
.fini = sdl_audio_fini,
.pcm_ops = &sdl_pcm_ops,
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index 6ad0eafbc6..4f7873af5a 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -77,7 +77,7 @@ static const SpiceRecordInterface record_sif = {
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
};
-static void *spice_audio_init (void)
+static void *spice_audio_init(Audiodev *dev)
{
if (!using_spice) {
return NULL;
@@ -130,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
#endif
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
- settings.fmt = AUD_FMT_S16;
+ settings.fmt = AUDIO_FORMAT_S16;
settings.endianness = AUDIO_HOST_ENDIANNESS;
audio_pcm_init_info (&hw->info, &settings);
@@ -258,7 +258,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
settings.freq = SPICE_INTERFACE_RECORD_FREQ;
#endif
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
- settings.fmt = AUD_FMT_S16;
+ settings.fmt = AUDIO_FORMAT_S16;
settings.endianness = AUDIO_HOST_ENDIANNESS;
audio_pcm_init_info (&hw->info, &settings);
@@ -373,10 +373,6 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
return 0;
}
-static struct audio_option audio_options[] = {
- { /* end of list */ },
-};
-
static struct audio_pcm_ops audio_callbacks = {
.init_out = line_out_init,
.fini_out = line_out_fini,
@@ -394,7 +390,6 @@ static struct audio_pcm_ops audio_callbacks = {
static struct audio_driver spice_audio_driver = {
.name = "spice",
.descr = "spice audio driver",
- .options = audio_options,
.init = spice_audio_init,
.fini = spice_audio_fini,
.pcm_ops = &audio_callbacks,
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 40adfa30c3..8d30f57296 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -24,6 +24,7 @@
#include "qemu/osdep.h"
#include "qemu/host-utils.h"
#include "qemu/timer.h"
+#include "qapi/opts-visitor.h"
#include "audio.h"
#define AUDIO_CAP "wav"
@@ -37,11 +38,6 @@ typedef struct WAVVoiceOut {
int total_samples;
} WAVVoiceOut;
-typedef struct {
- struct audsettings settings;
- const char *wav_path;
-} WAVConf;
-
static int wav_run_out (HWVoiceOut *hw, int live)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
@@ -112,25 +108,30 @@ 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
};
- WAVConf *conf = drv_opaque;
- struct audsettings wav_as = conf->settings;
+ Audiodev *dev = drv_opaque;
+ AudiodevWavOptions *wopts = &dev->u.wav;
+ struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
+ const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
stereo = wav_as.nchannels == 2;
switch (wav_as.fmt) {
- case AUD_FMT_S8:
- case AUD_FMT_U8:
+ case AUDIO_FORMAT_S8:
+ case AUDIO_FORMAT_U8:
bits16 = 0;
break;
- case AUD_FMT_S16:
- case AUD_FMT_U16:
+ case AUDIO_FORMAT_S16:
+ case AUDIO_FORMAT_U16:
bits16 = 1;
break;
- case AUD_FMT_S32:
- case AUD_FMT_U32:
+ case AUDIO_FORMAT_S32:
+ case AUDIO_FORMAT_U32:
dolog ("WAVE files can not handle 32bit formats\n");
return -1;
+
+ default:
+ abort();
}
hdr[34] = bits16 ? 0x10 : 0x08;
@@ -151,10 +152,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(wav_path, "wb");
if (!wav->f) {
dolog ("Failed to open wave file `%s'\nReason: %s\n",
- conf->wav_path, strerror (errno));
+ wav_path, strerror(errno));
g_free (wav->pcm_buf);
wav->pcm_buf = NULL;
return -1;
@@ -222,54 +223,17 @@ 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)
+static void *wav_audio_init(Audiodev *dev)
{
- WAVConf *conf = g_malloc(sizeof(WAVConf));
- *conf = glob_conf;
- return conf;
+ assert(dev->driver == AUDIODEV_DRIVER_WAV);
+ return dev;
}
static void wav_audio_fini (void *opaque)
{
ldebug ("wav_fini");
- g_free(opaque);
}
-static struct audio_option wav_options[] = {
- {
- .name = "FREQUENCY",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.settings.freq,
- .descr = "Frequency"
- },
- {
- .name = "FORMAT",
- .tag = AUD_OPT_FMT,
- .valp = &glob_conf.settings.fmt,
- .descr = "Format"
- },
- {
- .name = "DAC_FIXED_CHANNELS",
- .tag = AUD_OPT_INT,
- .valp = &glob_conf.settings.nchannels,
- .descr = "Number of channels (1 - mono, 2 - stereo)"
- },
- {
- .name = "PATH",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.wav_path,
- .descr = "Path to wave file"
- },
- { /* End of list */ }
-};
-
static struct audio_pcm_ops wav_pcm_ops = {
.init_out = wav_init_out,
.fini_out = wav_fini_out,
@@ -281,7 +245,6 @@ static struct audio_pcm_ops wav_pcm_ops = {
static struct audio_driver wav_audio_driver = {
.name = "wav",
.descr = "WAV renderer http://wikipedia.org/wiki/WAV",
- .options = wav_options,
.init = wav_audio_init,
.fini = wav_audio_fini,
.pcm_ops = &wav_pcm_ops,
diff --git a/audio/wavcapture.c b/audio/wavcapture.c
index cd24570aa7..74320dfecc 100644
--- a/audio/wavcapture.c
+++ b/audio/wavcapture.c
@@ -136,7 +136,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
as.freq = freq;
as.nchannels = 1 << stereo;
- as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
+ as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8;
as.endianness = 0;
ops.notify = wav_notify;
diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c
index ce54788048..37ac6445d2 100644
--- a/backends/hostmem-file.c
+++ b/backends/hostmem-file.c
@@ -56,6 +56,29 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
error_setg(errp, "mem-path property not set");
return;
}
+
+ /*
+ * Verify pmem file size since starting a guest with an incorrect size
+ * leads to confusing failures inside the guest.
+ */
+ if (fb->is_pmem) {
+ Error *local_err = NULL;
+ uint64_t size;
+
+ size = qemu_get_pmem_size(fb->mem_path, &local_err);
+ if (!size) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (backend->size > size) {
+ error_setg(errp, "size property %" PRIu64 " is larger than "
+ "pmem file \"%s\" size %" PRIu64, backend->size,
+ fb->mem_path, size);
+ return;
+ }
+ }
+
backend->force_prealloc = mem_prealloc;
name = host_memory_backend_get_name(backend);
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c
index 98c9bf3240..46b15b916a 100644
--- a/backends/hostmem-memfd.c
+++ b/backends/hostmem-memfd.c
@@ -154,15 +154,13 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
"Huge pages size (ex: 2M, 1G)",
&error_abort);
}
- if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
- object_class_property_add_bool(oc, "seal",
- memfd_backend_get_seal,
- memfd_backend_set_seal,
- &error_abort);
- object_class_property_set_description(oc, "seal",
- "Seal growing & shrinking",
- &error_abort);
- }
+ object_class_property_add_bool(oc, "seal",
+ memfd_backend_get_seal,
+ memfd_backend_set_seal,
+ &error_abort);
+ object_class_property_set_description(oc, "seal",
+ "Seal growing & shrinking",
+ &error_abort);
}
static const TypeInfo memfd_backend_info = {
@@ -175,7 +173,7 @@ static const TypeInfo memfd_backend_info = {
static void register_types(void)
{
- if (qemu_memfd_check(0)) {
+ if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
type_register_static(&memfd_backend_info);
}
}
diff --git a/block.c b/block.c
index ccf008c177..ed9253c786 100644
--- a/block.c
+++ b/block.c
@@ -1163,13 +1163,6 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
*/
open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_PROTOCOL);
- /*
- * Snapshots should be writable.
- */
- if (flags & BDRV_O_TEMPORARY) {
- open_flags |= BDRV_O_RDWR;
- }
-
return open_flags;
}
@@ -1698,6 +1691,7 @@ static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
typedef struct BlockReopenQueueEntry {
bool prepared;
+ bool perms_checked;
BDRVReopenState state;
QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry;
} BlockReopenQueueEntry;
@@ -2132,6 +2126,8 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
BlockDriverState *old_bs = child->bs;
int i;
+ assert(!child->frozen);
+
if (old_bs && new_bs) {
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
}
@@ -2348,6 +2344,10 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
bdrv_inherits_from_recursive(backing_hd, bs);
+ if (bdrv_is_backing_chain_frozen(bs, backing_bs(bs), errp)) {
+ return;
+ }
+
if (backing_hd) {
bdrv_ref(backing_hd);
}
@@ -2983,6 +2983,74 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
NULL, errp);
}
+/* Return true if the NULL-terminated @list contains @str */
+static bool is_str_in_list(const char *str, const char *const *list)
+{
+ if (str && list) {
+ int i;
+ for (i = 0; list[i] != NULL; i++) {
+ if (!strcmp(str, list[i])) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/*
+ * Check that every option set in @bs->options is also set in
+ * @new_opts.
+ *
+ * Options listed in the common_options list and in
+ * @bs->drv->mutable_opts are skipped.
+ *
+ * Return 0 on success, otherwise return -EINVAL and set @errp.
+ */
+static int bdrv_reset_options_allowed(BlockDriverState *bs,
+ const QDict *new_opts, Error **errp)
+{
+ const QDictEntry *e;
+ /* These options are common to all block drivers and are handled
+ * in bdrv_reopen_prepare() so they can be left out of @new_opts */
+ const char *const common_options[] = {
+ "node-name", "discard", "cache.direct", "cache.no-flush",
+ "read-only", "auto-read-only", "detect-zeroes", NULL
+ };
+
+ for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) {
+ if (!qdict_haskey(new_opts, e->key) &&
+ !is_str_in_list(e->key, common_options) &&
+ !is_str_in_list(e->key, bs->drv->mutable_opts)) {
+ error_setg(errp, "Option '%s' cannot be reset "
+ "to its default value", e->key);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Returns true if @child can be reached recursively from @bs
+ */
+static bool bdrv_recurse_has_child(BlockDriverState *bs,
+ BlockDriverState *child)
+{
+ BdrvChild *c;
+
+ if (bs == child) {
+ return true;
+ }
+
+ QLIST_FOREACH(c, &bs->children, next) {
+ if (bdrv_recurse_has_child(c->bs, child)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
/*
* Adds a BlockDriverState to a simple queue for an atomic, transactional
* reopen of multiple devices.
@@ -3010,7 +3078,8 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
QDict *options,
const BdrvChildRole *role,
QDict *parent_options,
- int parent_flags)
+ int parent_flags,
+ bool keep_old_opts)
{
assert(bs != NULL);
@@ -3050,13 +3119,13 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
*/
/* Old explicitly set values (don't overwrite by inherited value) */
- if (bs_entry) {
- old_options = qdict_clone_shallow(bs_entry->state.explicit_options);
- } else {
- old_options = qdict_clone_shallow(bs->explicit_options);
+ if (bs_entry || keep_old_opts) {
+ old_options = qdict_clone_shallow(bs_entry ?
+ bs_entry->state.explicit_options :
+ bs->explicit_options);
+ bdrv_join_options(bs, options, old_options);
+ qobject_unref(old_options);
}
- bdrv_join_options(bs, options, old_options);
- qobject_unref(old_options);
explicit_options = qdict_clone_shallow(options);
@@ -3068,10 +3137,12 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
flags = bdrv_get_flags(bs);
}
- /* Old values are used for options that aren't set yet */
- old_options = qdict_clone_shallow(bs->options);
- bdrv_join_options(bs, options, old_options);
- qobject_unref(old_options);
+ if (keep_old_opts) {
+ /* Old values are used for options that aren't set yet */
+ old_options = qdict_clone_shallow(bs->options);
+ bdrv_join_options(bs, options, old_options);
+ qobject_unref(old_options);
+ }
/* We have the final set of options so let's update the flags */
options_copy = qdict_clone_shallow(options);
@@ -3104,9 +3175,21 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
bs_entry->state.perm = UINT64_MAX;
bs_entry->state.shared_perm = 0;
+ /*
+ * If keep_old_opts is false then it means that unspecified
+ * options must be reset to their original value. We don't allow
+ * resetting 'backing' but we need to know if the option is
+ * missing in order to decide if we have to return an error.
+ */
+ if (!keep_old_opts) {
+ bs_entry->state.backing_missing =
+ !qdict_haskey(options, "backing") &&
+ !qdict_haskey(options, "backing.driver");
+ }
+
QLIST_FOREACH(child, &bs->children, next) {
- QDict *new_child_options;
- char *child_key_dot;
+ QDict *new_child_options = NULL;
+ bool child_keep_old = keep_old_opts;
/* reopen can only change the options of block devices that were
* implicitly created and inherited options. For other (referenced)
@@ -3115,13 +3198,32 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
continue;
}
- child_key_dot = g_strdup_printf("%s.", child->name);
- qdict_extract_subqdict(explicit_options, NULL, child_key_dot);
- qdict_extract_subqdict(options, &new_child_options, child_key_dot);
- g_free(child_key_dot);
+ /* Check if the options contain a child reference */
+ if (qdict_haskey(options, child->name)) {
+ const char *childref = qdict_get_try_str(options, child->name);
+ /*
+ * The current child must not be reopened if the child
+ * reference is null or points to a different node.
+ */
+ if (g_strcmp0(childref, child->bs->node_name)) {
+ continue;
+ }
+ /*
+ * If the child reference points to the current child then
+ * reopen it with its existing set of options (note that
+ * it can still inherit new options from the parent).
+ */
+ child_keep_old = true;
+ } else {
+ /* Extract child options ("child-name.*") */
+ char *child_key_dot = g_strdup_printf("%s.", child->name);
+ qdict_extract_subqdict(explicit_options, NULL, child_key_dot);
+ qdict_extract_subqdict(options, &new_child_options, child_key_dot);
+ g_free(child_key_dot);
+ }
bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options,
- child->role, options, flags);
+ child->role, options, flags, child_keep_old);
}
return bs_queue;
@@ -3129,9 +3231,10 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
BlockDriverState *bs,
- QDict *options)
+ QDict *options, bool keep_old_opts)
{
- return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, NULL, 0);
+ return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, NULL, 0,
+ keep_old_opts);
}
/*
@@ -3151,23 +3254,44 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
* All affected nodes must be drained between bdrv_reopen_queue() and
* bdrv_reopen_multiple().
*/
-int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
+int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
{
int ret = -1;
BlockReopenQueueEntry *bs_entry, *next;
- Error *local_err = NULL;
assert(bs_queue != NULL);
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
assert(bs_entry->state.bs->quiesce_counter > 0);
- if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
- error_propagate(errp, local_err);
+ if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
goto cleanup;
}
bs_entry->prepared = true;
}
+ QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
+ BDRVReopenState *state = &bs_entry->state;
+ ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
+ state->shared_perm, NULL, errp);
+ if (ret < 0) {
+ goto cleanup_perm;
+ }
+ /* Check if new_backing_bs would accept the new permissions */
+ if (state->replace_backing_bs && state->new_backing_bs) {
+ uint64_t nperm, nshared;
+ bdrv_child_perm(state->bs, state->new_backing_bs,
+ NULL, &child_backing, bs_queue,
+ state->perm, state->shared_perm,
+ &nperm, &nshared);
+ ret = bdrv_check_update_perm(state->new_backing_bs, NULL,
+ nperm, nshared, NULL, errp);
+ if (ret < 0) {
+ goto cleanup_perm;
+ }
+ }
+ bs_entry->perms_checked = true;
+ }
+
/* If we reach this point, we have success and just need to apply the
* changes
*/
@@ -3176,7 +3300,23 @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
}
ret = 0;
+cleanup_perm:
+ QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
+ BDRVReopenState *state = &bs_entry->state;
+
+ if (!bs_entry->perms_checked) {
+ continue;
+ }
+ if (ret == 0) {
+ bdrv_set_perm(state->bs, state->perm, state->shared_perm);
+ } else {
+ bdrv_abort_perm_update(state->bs);
+ if (state->replace_backing_bs && state->new_backing_bs) {
+ bdrv_abort_perm_update(state->new_backing_bs);
+ }
+ }
+ }
cleanup:
QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
if (ret) {
@@ -3186,6 +3326,9 @@ cleanup:
qobject_unref(bs_entry->state.explicit_options);
qobject_unref(bs_entry->state.options);
}
+ if (bs_entry->state.new_backing_bs) {
+ bdrv_unref(bs_entry->state.new_backing_bs);
+ }
g_free(bs_entry);
}
g_free(bs_queue);
@@ -3203,8 +3346,8 @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only);
bdrv_subtree_drained_begin(bs);
- queue = bdrv_reopen_queue(NULL, bs, opts);
- ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, errp);
+ queue = bdrv_reopen_queue(NULL, bs, opts, true);
+ ret = bdrv_reopen_multiple(queue, errp);
bdrv_subtree_drained_end(bs);
return ret;
@@ -3258,6 +3401,101 @@ static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs,
}
/*
+ * Take a BDRVReopenState and check if the value of 'backing' in the
+ * reopen_state->options QDict is valid or not.
+ *
+ * If 'backing' is missing from the QDict then return 0.
+ *
+ * If 'backing' contains the node name of the backing file of
+ * reopen_state->bs then return 0.
+ *
+ * If 'backing' contains a different node name (or is null) then check
+ * whether the current backing file can be replaced with the new one.
+ * If that's the case then reopen_state->replace_backing_bs is set to
+ * true and reopen_state->new_backing_bs contains a pointer to the new
+ * backing BlockDriverState (or NULL).
+ *
+ * Return 0 on success, otherwise return < 0 and set @errp.
+ */
+static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
+ Error **errp)
+{
+ BlockDriverState *bs = reopen_state->bs;
+ BlockDriverState *overlay_bs, *new_backing_bs;
+ QObject *value;
+ const char *str;
+
+ value = qdict_get(reopen_state->options, "backing");
+ if (value == NULL) {
+ return 0;
+ }
+
+ switch (qobject_type(value)) {
+ case QTYPE_QNULL:
+ new_backing_bs = NULL;
+ break;
+ case QTYPE_QSTRING:
+ str = qobject_get_try_str(value);
+ new_backing_bs = bdrv_lookup_bs(NULL, str, errp);
+ if (new_backing_bs == NULL) {
+ return -EINVAL;
+ } else if (bdrv_recurse_has_child(new_backing_bs, bs)) {
+ error_setg(errp, "Making '%s' a backing file of '%s' "
+ "would create a cycle", str, bs->node_name);
+ return -EINVAL;
+ }
+ break;
+ default:
+ /* 'backing' does not allow any other data type */
+ g_assert_not_reached();
+ }
+
+ /*
+ * TODO: before removing the x- prefix from x-blockdev-reopen we
+ * should move the new backing file into the right AioContext
+ * instead of returning an error.
+ */
+ if (new_backing_bs) {
+ if (bdrv_get_aio_context(new_backing_bs) != bdrv_get_aio_context(bs)) {
+ error_setg(errp, "Cannot use a new backing file "
+ "with a different AioContext");
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Find the "actual" backing file by skipping all links that point
+ * to an implicit node, if any (e.g. a commit filter node).
+ */
+ overlay_bs = bs;
+ while (backing_bs(overlay_bs) && backing_bs(overlay_bs)->implicit) {
+ overlay_bs = backing_bs(overlay_bs);
+ }
+
+ /* If we want to replace the backing file we need some extra checks */
+ if (new_backing_bs != backing_bs(overlay_bs)) {
+ /* Check for implicit nodes between bs and its backing file */
+ if (bs != overlay_bs) {
+ error_setg(errp, "Cannot change backing link if '%s' has "
+ "an implicit backing file", bs->node_name);
+ return -EPERM;
+ }
+ /* Check if the backing link that we want to replace is frozen */
+ if (bdrv_is_backing_chain_frozen(overlay_bs, backing_bs(overlay_bs),
+ errp)) {
+ return -EPERM;
+ }
+ reopen_state->replace_backing_bs = true;
+ if (new_backing_bs) {
+ bdrv_ref(new_backing_bs);
+ reopen_state->new_backing_bs = new_backing_bs;
+ }
+ }
+
+ return 0;
+}
+
+/*
* Prepares a BlockDriverState for reopen. All changes are staged in the
* 'opaque' field of the BDRVReopenState, which is used and allocated by
* the block driver layer .bdrv_reopen_prepare()
@@ -3355,6 +3593,17 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
}
if (drv->bdrv_reopen_prepare) {
+ /*
+ * If a driver-specific option is missing, it means that we
+ * should reset it to its default value.
+ * But not all options allow that, so we need to check it first.
+ */
+ ret = bdrv_reset_options_allowed(reopen_state->bs,
+ reopen_state->options, errp);
+ if (ret) {
+ goto error;
+ }
+
ret = drv->bdrv_reopen_prepare(reopen_state, queue, &local_err);
if (ret) {
if (local_err != NULL) {
@@ -3378,6 +3627,30 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
drv_prepared = true;
+ /*
+ * We must provide the 'backing' option if the BDS has a backing
+ * file or if the image file has a backing file name as part of
+ * its metadata. Otherwise the 'backing' option can be omitted.
+ */
+ if (drv->supports_backing && reopen_state->backing_missing &&
+ (backing_bs(reopen_state->bs) || reopen_state->bs->backing_file[0])) {
+ error_setg(errp, "backing is missing for '%s'",
+ reopen_state->bs->node_name);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /*
+ * Allow changing the 'backing' option. The new value can be
+ * either a reference to an existing node (using its node name)
+ * or NULL to simply detach the current backing file.
+ */
+ ret = bdrv_reopen_parse_backing(reopen_state, errp);
+ if (ret < 0) {
+ goto error;
+ }
+ qdict_del(reopen_state->options, "backing");
+
/* Options that are not handled are only okay if they are unchanged
* compared to the old state. It is expected that some options are only
* used for the initial open, but not reopen (e.g. filename) */
@@ -3430,12 +3703,6 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
} while ((entry = qdict_next(reopen_state->options, entry)));
}
- ret = bdrv_check_perm(reopen_state->bs, queue, reopen_state->perm,
- reopen_state->shared_perm, NULL, errp);
- if (ret < 0) {
- goto error;
- }
-
ret = 0;
/* Restore the original reopen_state->options QDict */
@@ -3493,6 +3760,11 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
bs->read_only = !(reopen_state->flags & BDRV_O_RDWR);
bs->detect_zeroes = reopen_state->detect_zeroes;
+ if (reopen_state->replace_backing_bs) {
+ qdict_del(bs->explicit_options, "backing");
+ qdict_del(bs->options, "backing");
+ }
+
/* Remove child references from bs->options and bs->explicit_options.
* Child options were already removed in bdrv_reopen_queue_child() */
QLIST_FOREACH(child, &bs->children, next) {
@@ -3500,10 +3772,22 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
qdict_del(bs->options, child->name);
}
- bdrv_refresh_limits(bs, NULL);
+ /*
+ * Change the backing file if a new one was specified. We do this
+ * after updating bs->options, so bdrv_refresh_filename() (called
+ * from bdrv_set_backing_hd()) has the new values.
+ */
+ if (reopen_state->replace_backing_bs) {
+ BlockDriverState *old_backing_bs = backing_bs(bs);
+ assert(!old_backing_bs || !old_backing_bs->implicit);
+ /* Abort the permission update on the backing bs we're detaching */
+ if (old_backing_bs) {
+ bdrv_abort_perm_update(old_backing_bs);
+ }
+ bdrv_set_backing_hd(bs, reopen_state->new_backing_bs, &error_abort);
+ }
- bdrv_set_perm(reopen_state->bs, reopen_state->perm,
- reopen_state->shared_perm);
+ bdrv_refresh_limits(bs, NULL);
new_can_write =
!bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE);
@@ -3536,8 +3820,6 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state)
if (drv->bdrv_reopen_abort) {
drv->bdrv_reopen_abort(reopen_state);
}
-
- bdrv_abort_perm_update(reopen_state->bs);
}
@@ -3717,6 +3999,11 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
if (!should_update_child(c, to)) {
continue;
}
+ if (c->frozen) {
+ error_setg(errp, "Cannot change '%s' link to '%s'",
+ c->name, from->node_name);
+ goto out;
+ }
list = g_slist_prepend(list, c);
perm |= c->perm;
shared &= c->shared_perm;
@@ -3929,6 +4216,63 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs)
}
/*
+ * Return true if at least one of the backing links between @bs and
+ * @base is frozen. @errp is set if that's the case.
+ */
+bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base,
+ Error **errp)
+{
+ BlockDriverState *i;
+
+ for (i = bs; i != base && i->backing; i = backing_bs(i)) {
+ if (i->backing->frozen) {
+ error_setg(errp, "Cannot change '%s' link from '%s' to '%s'",
+ i->backing->name, i->node_name,
+ backing_bs(i)->node_name);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * Freeze all backing links between @bs and @base.
+ * If any of the links is already frozen the operation is aborted and
+ * none of the links are modified.
+ * Returns 0 on success. On failure returns < 0 and sets @errp.
+ */
+int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base,
+ Error **errp)
+{
+ BlockDriverState *i;
+
+ if (bdrv_is_backing_chain_frozen(bs, base, errp)) {
+ return -EPERM;
+ }
+
+ for (i = bs; i != base && i->backing; i = backing_bs(i)) {
+ i->backing->frozen = true;
+ }
+
+ return 0;
+}
+
+/*
+ * Unfreeze all backing links between @bs and @base. The caller must
+ * ensure that all links are frozen before using this function.
+ */
+void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base)
+{
+ BlockDriverState *i;
+
+ for (i = bs; i != base && i->backing; i = backing_bs(i)) {
+ assert(i->backing->frozen);
+ i->backing->frozen = false;
+ }
+}
+
+/*
* Drops images above 'base' up to and including 'top', and sets the image
* above 'top' to have base as its backing file.
*
@@ -3977,6 +4321,14 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
goto exit;
}
+ /* This function changes all links that point to top and makes
+ * them point to base. Check that none of them is frozen. */
+ QLIST_FOREACH(c, &top->parents, next_parent) {
+ if (c->frozen) {
+ goto exit;
+ }
+ }
+
/* If 'base' recursively inherits from 'top' then we should set
* base->inherits_from to top->inherits_from after 'top' and all
* other intermediate nodes have been dropped.
diff --git a/block/commit.c b/block/commit.c
index 3b46ca7f97..ba60fef58a 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -39,6 +39,7 @@ typedef struct CommitBlockJob {
BlockDriverState *base_bs;
BlockdevOnError on_error;
bool base_read_only;
+ bool chain_frozen;
char *backing_file_str;
} CommitBlockJob;
@@ -68,6 +69,9 @@ static int commit_prepare(Job *job)
{
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
+ bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs);
+ s->chain_frozen = false;
+
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
* the normal backing chain can be restored. */
blk_unref(s->base);
@@ -84,6 +88,10 @@ static void commit_abort(Job *job)
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
BlockDriverState *top_bs = blk_bs(s->top);
+ if (s->chain_frozen) {
+ bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs);
+ }
+
/* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
bdrv_ref(top_bs);
bdrv_ref(s->commit_top_bs);
@@ -330,6 +338,11 @@ void commit_start(const char *job_id, BlockDriverState *bs,
}
}
+ if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) {
+ goto fail;
+ }
+ s->chain_frozen = true;
+
ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp);
if (ret < 0) {
goto fail;
@@ -362,6 +375,9 @@ void commit_start(const char *job_id, BlockDriverState *bs,
return;
fail:
+ if (s->chain_frozen) {
+ bdrv_unfreeze_backing_chain(commit_top_bs, base);
+ }
if (s->base) {
blk_unref(s->base);
}
diff --git a/block/file-posix.c b/block/file-posix.c
index ba6ab62a38..e9fa6aac48 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -144,6 +144,9 @@ typedef struct BDRVRawState {
uint64_t locked_perm;
uint64_t locked_shared_perm;
+ int perm_change_fd;
+ BDRVReopenState *reopen_state;
+
#ifdef CONFIG_XFS
bool is_xfs:1;
#endif
@@ -373,13 +376,21 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp)
}
}
-static void raw_parse_flags(int bdrv_flags, int *open_flags)
+static void raw_parse_flags(int bdrv_flags, int *open_flags, bool has_writers)
{
+ bool read_write = false;
assert(open_flags != NULL);
*open_flags |= O_BINARY;
*open_flags &= ~O_ACCMODE;
- if (bdrv_flags & BDRV_O_RDWR) {
+
+ if (bdrv_flags & BDRV_O_AUTO_RDONLY) {
+ read_write = has_writers;
+ } else if (bdrv_flags & BDRV_O_RDWR) {
+ read_write = true;
+ }
+
+ if (read_write) {
*open_flags |= O_RDWR;
} else {
*open_flags |= O_RDONLY;
@@ -431,6 +442,8 @@ static QemuOptsList raw_runtime_opts = {
},
};
+static const char *const mutable_opts[] = { "x-check-cache-dropped", NULL };
+
static int raw_open_common(BlockDriverState *bs, QDict *options,
int bdrv_flags, int open_flags,
bool device, Error **errp)
@@ -515,24 +528,12 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
false);
s->open_flags = open_flags;
- raw_parse_flags(bdrv_flags, &s->open_flags);
+ raw_parse_flags(bdrv_flags, &s->open_flags, false);
s->fd = -1;
fd = qemu_open(filename, s->open_flags, 0644);
ret = fd < 0 ? -errno : 0;
- if (ret == -EACCES || ret == -EROFS) {
- /* Try to degrade to read-only, but if it doesn't work, still use the
- * normal error message. */
- if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
- bdrv_flags &= ~BDRV_O_RDWR;
- raw_parse_flags(bdrv_flags, &s->open_flags);
- assert(!(s->open_flags & O_CREAT));
- fd = qemu_open(filename, s->open_flags);
- ret = fd < 0 ? -errno : 0;
- }
- }
-
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not open '%s'", filename);
if (ret == -EROFS) {
@@ -842,13 +843,77 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
return ret;
}
+static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
+ int *open_flags, uint64_t perm, bool force_dup,
+ Error **errp)
+{
+ BDRVRawState *s = bs->opaque;
+ int fd = -1;
+ int ret;
+ bool has_writers = perm &
+ (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_RESIZE);
+ int fcntl_flags = O_APPEND | O_NONBLOCK;
+#ifdef O_NOATIME
+ fcntl_flags |= O_NOATIME;
+#endif
+
+ *open_flags = 0;
+ if (s->type == FTYPE_CD) {
+ *open_flags |= O_NONBLOCK;
+ }
+
+ raw_parse_flags(flags, open_flags, has_writers);
+
+#ifdef O_ASYNC
+ /* Not all operating systems have O_ASYNC, and those that don't
+ * will not let us track the state into rs->open_flags (typically
+ * you achieve the same effect with an ioctl, for example I_SETSIG
+ * on Solaris). But we do not use O_ASYNC, so that's fine.
+ */
+ assert((s->open_flags & O_ASYNC) == 0);
+#endif
+
+ if (!force_dup && *open_flags == s->open_flags) {
+ /* We're lucky, the existing fd is fine */
+ return s->fd;
+ }
+
+ if ((*open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
+ /* dup the original fd */
+ fd = qemu_dup(s->fd);
+ if (fd >= 0) {
+ ret = fcntl_setfl(fd, *open_flags);
+ if (ret) {
+ qemu_close(fd);
+ fd = -1;
+ }
+ }
+ }
+
+ /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
+ if (fd == -1) {
+ const char *normalized_filename = bs->filename;
+ ret = raw_normalize_devicepath(&normalized_filename, errp);
+ if (ret >= 0) {
+ assert(!(*open_flags & O_CREAT));
+ fd = qemu_open(normalized_filename, *open_flags);
+ if (fd == -1) {
+ error_setg_errno(errp, errno, "Could not reopen file");
+ return -1;
+ }
+ }
+ }
+
+ return fd;
+}
+
static int raw_reopen_prepare(BDRVReopenState *state,
BlockReopenQueue *queue, Error **errp)
{
BDRVRawState *s;
BDRVRawReopenState *rs;
QemuOpts *opts;
- int ret = 0;
+ int ret;
Error *local_err = NULL;
assert(state != NULL);
@@ -858,7 +923,6 @@ static int raw_reopen_prepare(BDRVReopenState *state,
state->opaque = g_new0(BDRVRawReopenState, 1);
rs = state->opaque;
- rs->fd = -1;
/* Handle options changes */
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
@@ -877,50 +941,12 @@ static int raw_reopen_prepare(BDRVReopenState *state,
* bdrv_reopen_prepare() will detect changes and complain. */
qemu_opts_to_qdict(opts, state->options);
- if (s->type == FTYPE_CD) {
- rs->open_flags |= O_NONBLOCK;
- }
-
- raw_parse_flags(state->flags, &rs->open_flags);
-
- int fcntl_flags = O_APPEND | O_NONBLOCK;
-#ifdef O_NOATIME
- fcntl_flags |= O_NOATIME;
-#endif
-
-#ifdef O_ASYNC
- /* Not all operating systems have O_ASYNC, and those that don't
- * will not let us track the state into rs->open_flags (typically
- * you achieve the same effect with an ioctl, for example I_SETSIG
- * on Solaris). But we do not use O_ASYNC, so that's fine.
- */
- assert((s->open_flags & O_ASYNC) == 0);
-#endif
-
- if ((rs->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
- /* dup the original fd */
- rs->fd = qemu_dup(s->fd);
- if (rs->fd >= 0) {
- ret = fcntl_setfl(rs->fd, rs->open_flags);
- if (ret) {
- qemu_close(rs->fd);
- rs->fd = -1;
- }
- }
- }
-
- /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
- if (rs->fd == -1) {
- const char *normalized_filename = state->bs->filename;
- ret = raw_normalize_devicepath(&normalized_filename, errp);
- if (ret >= 0) {
- assert(!(rs->open_flags & O_CREAT));
- rs->fd = qemu_open(normalized_filename, rs->open_flags);
- if (rs->fd == -1) {
- error_setg_errno(errp, errno, "Could not reopen file");
- ret = -1;
- }
- }
+ rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
+ state->perm, true, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -1;
+ goto out;
}
/* Fail already reopen_prepare() if we can't get a working O_DIRECT
@@ -928,13 +954,19 @@ static int raw_reopen_prepare(BDRVReopenState *state,
if (rs->fd != -1) {
raw_probe_alignment(state->bs, rs->fd, &local_err);
if (local_err) {
- qemu_close(rs->fd);
- rs->fd = -1;
error_propagate(errp, local_err);
ret = -EINVAL;
+ goto out_fd;
}
}
+ s->reopen_state = state;
+ ret = 0;
+out_fd:
+ if (ret < 0) {
+ qemu_close(rs->fd);
+ rs->fd = -1;
+ }
out:
qemu_opts_del(opts);
return ret;
@@ -944,29 +976,25 @@ static void raw_reopen_commit(BDRVReopenState *state)
{
BDRVRawReopenState *rs = state->opaque;
BDRVRawState *s = state->bs->opaque;
- Error *local_err = NULL;
s->check_cache_dropped = rs->check_cache_dropped;
s->open_flags = rs->open_flags;
- /* Copy locks to the new fd before closing the old one. */
- raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm,
- s->locked_shared_perm, false, &local_err);
- if (local_err) {
- /* shouldn't fail in a sane host, but report it just in case. */
- error_report_err(local_err);
- }
qemu_close(s->fd);
s->fd = rs->fd;
g_free(state->opaque);
state->opaque = NULL;
+
+ assert(s->reopen_state == state);
+ s->reopen_state = NULL;
}
static void raw_reopen_abort(BDRVReopenState *state)
{
BDRVRawReopenState *rs = state->opaque;
+ BDRVRawState *s = state->bs->opaque;
/* nothing to do if NULL, we didn't get far enough */
if (rs == NULL) {
@@ -979,6 +1007,9 @@ static void raw_reopen_abort(BDRVReopenState *state)
}
g_free(state->opaque);
state->opaque = NULL;
+
+ assert(s->reopen_state == state);
+ s->reopen_state = NULL;
}
static int hdev_get_max_transfer_length(BlockDriverState *bs, int fd)
@@ -2664,12 +2695,78 @@ static QemuOptsList raw_create_opts = {
static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
Error **errp)
{
- return raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp);
+ BDRVRawState *s = bs->opaque;
+ BDRVRawReopenState *rs = NULL;
+ int open_flags;
+ int ret;
+
+ if (s->perm_change_fd) {
+ /*
+ * In the context of reopen, this function may be called several times
+ * (directly and recursively while change permissions of the parent).
+ * This is even true for children that don't inherit from the original
+ * reopen node, so s->reopen_state is not set.
+ *
+ * Ignore all but the first call.
+ */
+ return 0;
+ }
+
+ if (s->reopen_state) {
+ /* We already have a new file descriptor to set permissions for */
+ assert(s->reopen_state->perm == perm);
+ assert(s->reopen_state->shared_perm == shared);
+ rs = s->reopen_state->opaque;
+ s->perm_change_fd = rs->fd;
+ } else {
+ /* We may need a new fd if auto-read-only switches the mode */
+ ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, perm,
+ false, errp);
+ if (ret < 0) {
+ return ret;
+ } else if (ret != s->fd) {
+ s->perm_change_fd = ret;
+ }
+ }
+
+ /* Prepare permissions on old fd to avoid conflicts between old and new,
+ * but keep everything locked that new will need. */
+ ret = raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ /* Copy locks to the new fd */
+ if (s->perm_change_fd) {
+ ret = raw_apply_lock_bytes(NULL, s->perm_change_fd, perm, ~shared,
+ false, errp);
+ if (ret < 0) {
+ raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
+ goto fail;
+ }
+ }
+ return 0;
+
+fail:
+ if (s->perm_change_fd && !s->reopen_state) {
+ qemu_close(s->perm_change_fd);
+ }
+ s->perm_change_fd = 0;
+ return ret;
}
static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
{
BDRVRawState *s = bs->opaque;
+
+ /* For reopen, we have already switched to the new fd (.bdrv_set_perm is
+ * called after .bdrv_reopen_commit) */
+ if (s->perm_change_fd && s->fd != s->perm_change_fd) {
+ qemu_close(s->fd);
+ s->fd = s->perm_change_fd;
+ }
+ s->perm_change_fd = 0;
+
raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL);
s->perm = perm;
s->shared_perm = shared;
@@ -2677,6 +2774,15 @@ static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
static void raw_abort_perm_update(BlockDriverState *bs)
{
+ BDRVRawState *s = bs->opaque;
+
+ /* For reopen, .bdrv_reopen_abort is called afterwards and will close
+ * the file descriptor. */
+ if (s->perm_change_fd && !s->reopen_state) {
+ qemu_close(s->perm_change_fd);
+ }
+ s->perm_change_fd = 0;
+
raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
}
@@ -2766,6 +2872,7 @@ BlockDriver bdrv_file = {
.bdrv_set_perm = raw_set_perm,
.bdrv_abort_perm_update = raw_abort_perm_update,
.create_opts = &raw_create_opts,
+ .mutable_opts = mutable_opts,
};
/***********************************************/
@@ -3217,6 +3324,7 @@ static BlockDriver bdrv_host_device = {
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_co_create_opts = hdev_co_create_opts,
.create_opts = &raw_create_opts,
+ .mutable_opts = mutable_opts,
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
.bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes,
@@ -3343,6 +3451,7 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_co_create_opts = hdev_co_create_opts,
.create_opts = &raw_create_opts,
+ .mutable_opts = mutable_opts,
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
@@ -3476,6 +3585,7 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_co_create_opts = hdev_co_create_opts,
.create_opts = &raw_create_opts,
+ .mutable_opts = mutable_opts,
.bdrv_co_preadv = raw_co_preadv,
.bdrv_co_pwritev = raw_co_pwritev,
diff --git a/block/gluster.c b/block/gluster.c
index af64330211..51f184cbd8 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -20,6 +20,10 @@
#include "qemu/option.h"
#include "qemu/cutils.h"
+#ifdef CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT
+# define glfs_ftruncate(fd, offset) glfs_ftruncate(fd, offset, NULL, NULL)
+#endif
+
#define GLUSTER_OPT_FILENAME "filename"
#define GLUSTER_OPT_VOLUME "volume"
#define GLUSTER_OPT_PATH "path"
@@ -725,7 +729,11 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
/*
* AIO callback routine called from GlusterFS thread.
*/
-static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
+static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret,
+#ifdef CONFIG_GLUSTERFS_IOCB_HAS_STAT
+ struct glfs_stat *pre, struct glfs_stat *post,
+#endif
+ void *arg)
{
GlusterAIOCB *acb = (GlusterAIOCB *)arg;
diff --git a/block/mirror.c b/block/mirror.c
index 726d3c27fb..010fdafd79 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -630,6 +630,10 @@ static int mirror_exit_common(Job *job)
}
s->prepared = true;
+ if (bdrv_chain_contains(src, target_bs)) {
+ bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
+ }
+
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
/* Make sure that the source BDS doesn't go away during bdrv_replace_node,
@@ -1639,6 +1643,10 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
goto fail;
}
}
+
+ if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) {
+ goto fail;
+ }
}
QTAILQ_INIT(&s->ops_in_flight);
diff --git a/block/qapi.c b/block/qapi.c
index 6002a768f8..21edab34fc 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -493,14 +493,14 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
}
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ],
- &ds->has_x_rd_latency_histogram,
- &ds->x_rd_latency_histogram);
+ &ds->has_rd_latency_histogram,
+ &ds->rd_latency_histogram);
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE],
- &ds->has_x_wr_latency_histogram,
- &ds->x_wr_latency_histogram);
+ &ds->has_wr_latency_histogram,
+ &ds->wr_latency_histogram);
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH],
- &ds->has_x_flush_latency_histogram,
- &ds->x_flush_latency_histogram);
+ &ds->has_flush_latency_histogram,
+ &ds->flush_latency_histogram);
}
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
diff --git a/block/qcow2.c b/block/qcow2.c
index d48e2e56f7..0dd77c6367 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -627,6 +627,30 @@ int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
return 0;
}
+static const char *const mutable_opts[] = {
+ QCOW2_OPT_LAZY_REFCOUNTS,
+ QCOW2_OPT_DISCARD_REQUEST,
+ QCOW2_OPT_DISCARD_SNAPSHOT,
+ QCOW2_OPT_DISCARD_OTHER,
+ QCOW2_OPT_OVERLAP,
+ QCOW2_OPT_OVERLAP_TEMPLATE,
+ QCOW2_OPT_OVERLAP_MAIN_HEADER,
+ QCOW2_OPT_OVERLAP_ACTIVE_L1,
+ QCOW2_OPT_OVERLAP_ACTIVE_L2,
+ QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
+ QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
+ QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
+ QCOW2_OPT_OVERLAP_INACTIVE_L1,
+ QCOW2_OPT_OVERLAP_INACTIVE_L2,
+ QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY,
+ QCOW2_OPT_CACHE_SIZE,
+ QCOW2_OPT_L2_CACHE_SIZE,
+ QCOW2_OPT_L2_CACHE_ENTRY_SIZE,
+ QCOW2_OPT_REFCOUNT_CACHE_SIZE,
+ QCOW2_OPT_CACHE_CLEAN_INTERVAL,
+ NULL
+};
+
static QemuOptsList qcow2_runtime_opts = {
.name = "qcow2",
.head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head),
@@ -5273,6 +5297,7 @@ BlockDriver bdrv_qcow2 = {
.create_opts = &qcow2_create_opts,
.strong_runtime_opts = qcow2_strong_runtime_opts,
+ .mutable_opts = mutable_opts,
.bdrv_co_check = qcow2_co_check,
.bdrv_amend_options = qcow2_amend_options,
diff --git a/block/raw-format.c b/block/raw-format.c
index e3e5ba2c8a..cec29986cc 100644
--- a/block/raw-format.c
+++ b/block/raw-format.c
@@ -37,6 +37,8 @@ typedef struct BDRVRawState {
bool has_size;
} BDRVRawState;
+static const char *const mutable_opts[] = { "offset", "size", NULL };
+
static QemuOptsList raw_runtime_opts = {
.name = "raw",
.head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head),
@@ -570,6 +572,7 @@ BlockDriver bdrv_raw = {
.create_opts = &raw_create_opts,
.bdrv_has_zero_init = &raw_has_zero_init,
.strong_runtime_opts = raw_strong_runtime_opts,
+ .mutable_opts = mutable_opts,
};
static void bdrv_raw_init(void)
diff --git a/block/replication.c b/block/replication.c
index 4c80b54daf..b95bd28802 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -374,19 +374,18 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
QDict *opts = qdict_new();
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs,
- opts);
+ opts, true);
}
if (s->orig_secondary_read_only) {
QDict *opts = qdict_new();
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs,
- opts);
+ opts, true);
}
if (reopen_queue) {
- bdrv_reopen_multiple(bdrv_get_aio_context(bs),
- reopen_queue, &local_err);
+ bdrv_reopen_multiple(reopen_queue, &local_err);
error_propagate(errp, local_err);
}
diff --git a/block/stream.c b/block/stream.c
index e14579ff80..6253c86fae 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -35,6 +35,7 @@ typedef struct StreamBlockJob {
BlockdevOnError on_error;
char *backing_file_str;
bool bs_read_only;
+ bool chain_frozen;
} StreamBlockJob;
static int coroutine_fn stream_populate(BlockBackend *blk,
@@ -49,6 +50,16 @@ static int coroutine_fn stream_populate(BlockBackend *blk,
return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
}
+static void stream_abort(Job *job)
+{
+ StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
+
+ if (s->chain_frozen) {
+ BlockJob *bjob = &s->common;
+ bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->base);
+ }
+}
+
static int stream_prepare(Job *job)
{
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
@@ -58,6 +69,9 @@ static int stream_prepare(Job *job)
Error *local_err = NULL;
int ret = 0;
+ bdrv_unfreeze_backing_chain(bs, base);
+ s->chain_frozen = false;
+
if (bs->backing) {
const char *base_id = NULL, *base_fmt = NULL;
if (base) {
@@ -208,6 +222,7 @@ static const BlockJobDriver stream_job_driver = {
.free = block_job_free,
.run = stream_run,
.prepare = stream_prepare,
+ .abort = stream_abort,
.clean = stream_clean,
.user_resume = block_job_user_resume,
.drain = block_job_drain,
@@ -254,9 +269,15 @@ void stream_start(const char *job_id, BlockDriverState *bs,
&error_abort);
}
+ if (bdrv_freeze_backing_chain(bs, base, errp) < 0) {
+ job_early_fail(&s->common.job);
+ goto fail;
+ }
+
s->base = base;
s->backing_file_str = g_strdup(backing_file_str);
s->bs_read_only = bs_read_only;
+ s->chain_frozen = true;
s->on_error = on_error;
trace_stream_start(bs, base, s);
diff --git a/blockdev.c b/blockdev.c
index 4dbf20bb68..53df2eb875 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -4256,6 +4256,53 @@ fail:
visit_free(v);
}
+void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
+{
+ BlockDriverState *bs;
+ AioContext *ctx;
+ QObject *obj;
+ Visitor *v = qobject_output_visitor_new(&obj);
+ Error *local_err = NULL;
+ BlockReopenQueue *queue;
+ QDict *qdict;
+
+ /* Check for the selected node name */
+ if (!options->has_node_name) {
+ error_setg(errp, "Node name not specified");
+ goto fail;
+ }
+
+ bs = bdrv_find_node(options->node_name);
+ if (!bs) {
+ error_setg(errp, "Cannot find node named '%s'", options->node_name);
+ goto fail;
+ }
+
+ /* Put all options in a QDict and flatten it */
+ visit_type_BlockdevOptions(v, NULL, &options, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ visit_complete(v, &obj);
+ qdict = qobject_to(QDict, obj);
+
+ qdict_flatten(qdict);
+
+ /* Perform the reopen operation */
+ ctx = bdrv_get_aio_context(bs);
+ aio_context_acquire(ctx);
+ bdrv_subtree_drained_begin(bs);
+ queue = bdrv_reopen_queue(NULL, bs, qdict, false);
+ bdrv_reopen_multiple(queue, errp);
+ bdrv_subtree_drained_end(bs);
+ aio_context_release(ctx);
+
+fail:
+ visit_free(v);
+}
+
void qmp_blockdev_del(const char *node_name, Error **errp)
{
AioContext *aio_context;
@@ -4421,22 +4468,22 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
aio_context_release(old_context);
}
-void qmp_x_block_latency_histogram_set(
- const char *device,
+void qmp_block_latency_histogram_set(
+ const char *id,
bool has_boundaries, uint64List *boundaries,
bool has_boundaries_read, uint64List *boundaries_read,
bool has_boundaries_write, uint64List *boundaries_write,
bool has_boundaries_flush, uint64List *boundaries_flush,
Error **errp)
{
- BlockBackend *blk = blk_by_name(device);
+ BlockBackend *blk = qmp_get_blk(NULL, id, errp);
BlockAcctStats *stats;
int ret;
if (!blk) {
- error_setg(errp, "Device '%s' not found", device);
return;
}
+
stats = blk_get_stats(blk);
if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
@@ -4451,7 +4498,7 @@ void qmp_x_block_latency_histogram_set(
stats, BLOCK_ACCT_READ,
has_boundaries_read ? boundaries_read : boundaries);
if (ret) {
- error_setg(errp, "Device '%s' set read boundaries fail", device);
+ error_setg(errp, "Device '%s' set read boundaries fail", id);
return;
}
}
@@ -4461,7 +4508,7 @@ void qmp_x_block_latency_histogram_set(
stats, BLOCK_ACCT_WRITE,
has_boundaries_write ? boundaries_write : boundaries);
if (ret) {
- error_setg(errp, "Device '%s' set write boundaries fail", device);
+ error_setg(errp, "Device '%s' set write boundaries fail", id);
return;
}
}
@@ -4471,7 +4518,7 @@ void qmp_x_block_latency_histogram_set(
stats, BLOCK_ACCT_FLUSH,
has_boundaries_flush ? boundaries_flush : boundaries);
if (ret) {
- error_setg(errp, "Device '%s' set flush boundaries fail", device);
+ error_setg(errp, "Device '%s' set flush boundaries fail", id);
return;
}
}
diff --git a/configure b/configure
index cab830a4c9..45a3654c5b 100755
--- a/configure
+++ b/configure
@@ -456,6 +456,8 @@ glusterfs_xlator_opt="no"
glusterfs_discard="no"
glusterfs_fallocate="no"
glusterfs_zerofill="no"
+glusterfs_ftruncate_has_stat="no"
+glusterfs_iocb_has_stat="no"
gtk=""
gtk_gl="no"
tls_priority="NORMAL"
@@ -4091,6 +4093,38 @@ if test "$glusterfs" != "no" ; then
glusterfs_fallocate="yes"
glusterfs_zerofill="yes"
fi
+ cat > $TMPC << EOF
+#include <glusterfs/api/glfs.h>
+
+int
+main(void)
+{
+ /* new glfs_ftruncate() passes two additional args */
+ return glfs_ftruncate(NULL, 0, NULL, NULL);
+}
+EOF
+ if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then
+ glusterfs_ftruncate_has_stat="yes"
+ fi
+ cat > $TMPC << EOF
+#include <glusterfs/api/glfs.h>
+
+/* new glfs_io_cbk() passes two additional glfs_stat structs */
+static void
+glusterfs_iocb(glfs_fd_t *fd, ssize_t ret, struct glfs_stat *prestat, struct glfs_stat *poststat, void *data)
+{}
+
+int
+main(void)
+{
+ glfs_io_cbk iocb = &glusterfs_iocb;
+ iocb(NULL, 0 , NULL, NULL, NULL);
+ return 0;
+}
+EOF
+ if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then
+ glusterfs_iocb_has_stat="yes"
+ fi
else
if test "$glusterfs" = "yes" ; then
feature_not_found "GlusterFS backend support" \
@@ -6976,6 +7010,14 @@ if test "$glusterfs_zerofill" = "yes" ; then
echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak
fi
+if test "$glusterfs_ftruncate_has_stat" = "yes" ; then
+ echo "CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT=y" >> $config_host_mak
+fi
+
+if test "$glusterfs_iocb_has_stat" = "yes" ; then
+ echo "CONFIG_GLUSTERFS_IOCB_HAS_STAT=y" >> $config_host_mak
+fi
+
if test "$libssh2" = "yes" ; then
echo "CONFIG_LIBSSH2=m" >> $config_host_mak
echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak
diff --git a/docs/devel/decodetree.rst b/docs/devel/decodetree.rst
new file mode 100644
index 0000000000..44ac621ea8
--- /dev/null
+++ b/docs/devel/decodetree.rst
@@ -0,0 +1,221 @@
+========================
+Decodetree Specification
+========================
+
+A *decodetree* is built from instruction *patterns*. A pattern may
+represent a single architectural instruction or a group of same, depending
+on what is convenient for further processing.
+
+Each pattern has both *fixedbits* and *fixedmask*, the combination of which
+describes the condition under which the pattern is matched::
+
+ (insn & fixedmask) == fixedbits
+
+Each pattern may have *fields*, which are extracted from the insn and
+passed along to the translator. Examples of such are registers,
+immediates, and sub-opcodes.
+
+In support of patterns, one may declare *fields*, *argument sets*, and
+*formats*, each of which may be re-used to simplify further definitions.
+
+Fields
+======
+
+Syntax::
+
+ field_def := '%' identifier ( unnamed_field )+ ( !function=identifier )?
+ unnamed_field := number ':' ( 's' ) number
+
+For *unnamed_field*, the first number is the least-significant bit position
+of the field and the second number is the length of the field. If the 's' is
+present, the field is considered signed. If multiple ``unnamed_fields`` are
+present, they are concatenated. In this way one can define disjoint fields.
+
+If ``!function`` is specified, the concatenated result is passed through the
+named function, taking and returning an integral value.
+
+FIXME: the fields of the structure into which this result will be stored
+is restricted to ``int``. Which means that we cannot expand 64-bit items.
+
+Field examples:
+
++---------------------------+---------------------------------------------+
+| Input | Generated code |
++===========================+=============================================+
+| %disp 0:s16 | sextract(i, 0, 16) |
++---------------------------+---------------------------------------------+
+| %imm9 16:6 10:3 | extract(i, 16, 6) << 3 | extract(i, 10, 3) |
++---------------------------+---------------------------------------------+
+| %disp12 0:s1 1:1 2:10 | sextract(i, 0, 1) << 11 | |
+| | extract(i, 1, 1) << 10 | |
+| | extract(i, 2, 10) |
++---------------------------+---------------------------------------------+
+| %shimm8 5:s8 13:1 | expand_shimm8(sextract(i, 5, 8) << 1 | |
+| !function=expand_shimm8 | extract(i, 13, 1)) |
++---------------------------+---------------------------------------------+
+
+Argument Sets
+=============
+
+Syntax::
+
+ args_def := '&' identifier ( args_elt )+ ( !extern )?
+ args_elt := identifier
+
+Each *args_elt* defines an argument within the argument set.
+Each argument set will be rendered as a C structure "arg_$name"
+with each of the fields being one of the member arguments.
+
+If ``!extern`` is specified, the backing structure is assumed
+to have been already declared, typically via a second decoder.
+
+Argument sets are useful when one wants to define helper functions
+for the translator functions that can perform operations on a common
+set of arguments. This can ensure, for instance, that the ``AND``
+pattern and the ``OR`` pattern put their operands into the same named
+structure, so that a common ``gen_logic_insn`` may be able to handle
+the operations common between the two.
+
+Argument set examples::
+
+ &reg3 ra rb rc
+ &loadstore reg base offset
+
+
+Formats
+=======
+
+Syntax::
+
+ fmt_def := '@' identifier ( fmt_elt )+
+ fmt_elt := fixedbit_elt | field_elt | field_ref | args_ref
+ fixedbit_elt := [01.-]+
+ field_elt := identifier ':' 's'? number
+ field_ref := '%' identifier | identifier '=' '%' identifier
+ args_ref := '&' identifier
+
+Defining a format is a handy way to avoid replicating groups of fields
+across many instruction patterns.
+
+A *fixedbit_elt* describes a contiguous sequence of bits that must
+be 1, 0, or don't care. The difference between '.' and '-'
+is that '.' means that the bit will be covered with a field or a
+final 0 or 1 from the pattern, and '-' means that the bit is really
+ignored by the cpu and will not be specified.
+
+A *field_elt* describes a simple field only given a width; the position of
+the field is implied by its position with respect to other *fixedbit_elt*
+and *field_elt*.
+
+If any *fixedbit_elt* or *field_elt* appear, then all bits must be defined.
+Padding with a *fixedbit_elt* of all '.' is an easy way to accomplish that.
+
+A *field_ref* incorporates a field by reference. This is the only way to
+add a complex field to a format. A field may be renamed in the process
+via assignment to another identifier. This is intended to allow the
+same argument set be used with disjoint named fields.
+
+A single *args_ref* may specify an argument set to use for the format.
+The set of fields in the format must be a subset of the arguments in
+the argument set. If an argument set is not specified, one will be
+inferred from the set of fields.
+
+It is recommended, but not required, that all *field_ref* and *args_ref*
+appear at the end of the line, not interleaving with *fixedbit_elf* or
+*field_elt*.
+
+Format examples::
+
+ @opr ...... ra:5 rb:5 ... 0 ....... rc:5
+ @opi ...... ra:5 lit:8 1 ....... rc:5
+
+Patterns
+========
+
+Syntax::
+
+ pat_def := identifier ( pat_elt )+
+ pat_elt := fixedbit_elt | field_elt | field_ref | args_ref | fmt_ref | const_elt
+ fmt_ref := '@' identifier
+ const_elt := identifier '=' number
+
+The *fixedbit_elt* and *field_elt* specifiers are unchanged from formats.
+A pattern that does not specify a named format will have one inferred
+from a referenced argument set (if present) and the set of fields.
+
+A *const_elt* allows a argument to be set to a constant value. This may
+come in handy when fields overlap between patterns and one has to
+include the values in the *fixedbit_elt* instead.
+
+The decoder will call a translator function for each pattern matched.
+
+Pattern examples::
+
+ addl_r 010000 ..... ..... .... 0000000 ..... @opr
+ addl_i 010000 ..... ..... .... 0000000 ..... @opi
+
+which will, in part, invoke::
+
+ trans_addl_r(ctx, &arg_opr, insn)
+
+and::
+
+ trans_addl_i(ctx, &arg_opi, insn)
+
+Pattern Groups
+==============
+
+Syntax::
+
+ group := '{' ( pat_def | group )+ '}'
+
+A *group* begins with a lone open-brace, with all subsequent lines
+indented two spaces, and ending with a lone close-brace. Groups
+may be nested, increasing the required indentation of the lines
+within the nested group to two spaces per nesting level.
+
+Unlike ungrouped patterns, grouped patterns are allowed to overlap.
+Conflicts are resolved by selecting the patterns in order. If all
+of the fixedbits for a pattern match, its translate function will
+be called. If the translate function returns false, then subsequent
+patterns within the group will be matched.
+
+The following example from PA-RISC shows specialization of the *or*
+instruction::
+
+ {
+ {
+ nop 000010 ----- ----- 0000 001001 0 00000
+ copy 000010 00000 r1:5 0000 001001 0 rt:5
+ }
+ or 000010 rt2:5 r1:5 cf:4 001001 0 rt:5
+ }
+
+When the *cf* field is zero, the instruction has no side effects,
+and may be specialized. When the *rt* field is zero, the output
+is discarded and so the instruction has no effect. When the *rt2*
+field is zero, the operation is ``reg[rt] | 0`` and so encodes
+the canonical register copy operation.
+
+The output from the generator might look like::
+
+ switch (insn & 0xfc000fe0) {
+ case 0x08000240:
+ /* 000010.. ........ ....0010 010..... */
+ if ((insn & 0x0000f000) == 0x00000000) {
+ /* 000010.. ........ 00000010 010..... */
+ if ((insn & 0x0000001f) == 0x00000000) {
+ /* 000010.. ........ 00000010 01000000 */
+ extract_decode_Fmt_0(&u.f_decode0, insn);
+ if (trans_nop(ctx, &u.f_decode0)) return true;
+ }
+ if ((insn & 0x03e00000) == 0x00000000) {
+ /* 00001000 000..... 00000010 010..... */
+ extract_decode_Fmt_1(&u.f_decode1, insn);
+ if (trans_copy(ctx, &u.f_decode1)) return true;
+ }
+ }
+ extract_decode_Fmt_2(&u.f_decode2, insn);
+ if (trans_or(ctx, &u.f_decode2)) return true;
+ return false;
+ }
diff --git a/docs/devel/index.rst b/docs/devel/index.rst
index 6b11e49caa..ebbab636ce 100644
--- a/docs/devel/index.rst
+++ b/docs/devel/index.rst
@@ -19,4 +19,4 @@ Contents:
migration
stable-process
testing
-
+ decodetree
diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c
index e53b2cb681..f73cfb9d90 100644
--- a/hw/acpi/nvdimm.c
+++ b/hw/acpi/nvdimm.c
@@ -382,7 +382,7 @@ nvdimm_build_structure_caps(GArray *structures, uint32_t capabilities)
nfit_caps->capabilities = cpu_to_le32(capabilities);
}
-static GArray *nvdimm_build_device_structure(AcpiNVDIMMState *state)
+static GArray *nvdimm_build_device_structure(NVDIMMState *state)
{
GSList *device_list = nvdimm_get_device_list();
GArray *structures = g_array_new(false, true /* clear */, 1);
@@ -416,7 +416,7 @@ static void nvdimm_init_fit_buffer(NvdimmFitBuffer *fit_buf)
fit_buf->fit = g_array_new(false, true /* clear */, 1);
}
-static void nvdimm_build_fit_buffer(AcpiNVDIMMState *state)
+static void nvdimm_build_fit_buffer(NVDIMMState *state)
{
NvdimmFitBuffer *fit_buf = &state->fit_buf;
@@ -425,12 +425,12 @@ static void nvdimm_build_fit_buffer(AcpiNVDIMMState *state)
fit_buf->dirty = true;
}
-void nvdimm_plug(AcpiNVDIMMState *state)
+void nvdimm_plug(NVDIMMState *state)
{
nvdimm_build_fit_buffer(state);
}
-static void nvdimm_build_nfit(AcpiNVDIMMState *state, GArray *table_offsets,
+static void nvdimm_build_nfit(NVDIMMState *state, GArray *table_offsets,
GArray *table_data, BIOSLinker *linker)
{
NvdimmFitBuffer *fit_buf = &state->fit_buf;
@@ -570,7 +570,7 @@ nvdimm_dsm_no_payload(uint32_t func_ret_status, hwaddr dsm_mem_addr)
#define NVDIMM_QEMU_RSVD_HANDLE_ROOT 0x10000
/* Read FIT data, defined in docs/specs/acpi_nvdimm.txt. */
-static void nvdimm_dsm_func_read_fit(AcpiNVDIMMState *state, NvdimmDsmIn *in,
+static void nvdimm_dsm_func_read_fit(NVDIMMState *state, NvdimmDsmIn *in,
hwaddr dsm_mem_addr)
{
NvdimmFitBuffer *fit_buf = &state->fit_buf;
@@ -619,7 +619,7 @@ exit:
}
static void
-nvdimm_dsm_handle_reserved_root_method(AcpiNVDIMMState *state,
+nvdimm_dsm_handle_reserved_root_method(NVDIMMState *state,
NvdimmDsmIn *in, hwaddr dsm_mem_addr)
{
switch (in->function) {
@@ -863,7 +863,7 @@ nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size)
static void
nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
- AcpiNVDIMMState *state = opaque;
+ NVDIMMState *state = opaque;
NvdimmDsmIn *in;
hwaddr dsm_mem_addr = val;
@@ -925,7 +925,7 @@ void nvdimm_acpi_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev)
}
}
-void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io,
+void nvdimm_init_acpi_state(NVDIMMState *state, MemoryRegion *io,
FWCfgState *fw_cfg, Object *owner)
{
memory_region_init_io(&state->io_mr, owner, &nvdimm_dsm_ops, state,
@@ -1319,7 +1319,7 @@ static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data,
}
void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data,
- BIOSLinker *linker, AcpiNVDIMMState *state,
+ BIOSLinker *linker, NVDIMMState *state,
uint32_t ram_slots)
{
GSList *device_list;
diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c
index 94dffb2f57..446223906e 100644
--- a/hw/arm/omap2.c
+++ b/hw/arm/omap2.c
@@ -273,7 +273,7 @@ static void omap_eac_format_update(struct omap_eac_s *s)
* does I2S specify it? */
/* All register writes are 16 bits so we we store 16-bit samples
* in the buffers regardless of AGCFR[B8_16] value. */
- fmt.fmt = AUD_FMT_U16;
+ fmt.fmt = AUDIO_FORMAT_U16;
s->codec.in_voice = AUD_open_in(&s->codec.card, s->codec.in_voice,
"eac.codec.in", s, omap_eac_in_cb, &fmt);
diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c
index d799533aa9..2265622d44 100644
--- a/hw/audio/ac97.c
+++ b/hw/audio/ac97.c
@@ -365,7 +365,7 @@ static void open_voice (AC97LinkState *s, int index, int freq)
as.freq = freq;
as.nchannels = 2;
- as.fmt = AUD_FMT_S16;
+ as.fmt = AUDIO_FORMAT_S16;
as.endianness = 0;
if (freq > 0) {
diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c
index 97b876c7e0..0957780a3d 100644
--- a/hw/audio/adlib.c
+++ b/hw/audio/adlib.c
@@ -269,7 +269,7 @@ static void adlib_realizefn (DeviceState *dev, Error **errp)
as.freq = s->freq;
as.nchannels = SHIFT;
- as.fmt = AUD_FMT_S16;
+ as.fmt = AUDIO_FORMAT_S16;
as.endianness = AUDIO_HOST_ENDIANNESS;
AUD_register_card ("adlib", &s->card);
diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c
index 9089dcb47e..62da75eefe 100644
--- a/hw/audio/cs4231a.c
+++ b/hw/audio/cs4231a.c
@@ -288,7 +288,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) {
case 0:
- as.fmt = AUD_FMT_U8;
+ as.fmt = AUDIO_FORMAT_U8;
s->shift = as.nchannels == 2;
break;
@@ -298,7 +298,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
case 3:
s->tab = ALawDecompressTable;
x_law:
- as.fmt = AUD_FMT_S16;
+ as.fmt = AUDIO_FORMAT_S16;
as.endianness = AUDIO_HOST_ENDIANNESS;
s->shift = as.nchannels == 2;
break;
@@ -307,7 +307,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
as.endianness = 1;
/* fall through */
case 2:
- as.fmt = AUD_FMT_S16;
+ as.fmt = AUDIO_FORMAT_S16;
s->shift = as.nchannels;
break;
diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c
index 97789a0771..a5314d66fd 100644
--- a/hw/audio/es1370.c
+++ b/hw/audio/es1370.c
@@ -414,14 +414,14 @@ static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl)
i,
new_freq,
1 << (new_fmt & 1),
- (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8,
+ (new_fmt & 2) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8,
d->shift);
if (new_freq) {
struct audsettings as;
as.freq = new_freq;
as.nchannels = 1 << (new_fmt & 1);
- as.fmt = (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8;
+ as.fmt = (new_fmt & 2) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8;
as.endianness = 0;
if (i == ADC_CHANNEL) {
diff --git a/hw/audio/gus.c b/hw/audio/gus.c
index 8e0b27e0f2..b3e2a7fdd5 100644
--- a/hw/audio/gus.c
+++ b/hw/audio/gus.c
@@ -251,7 +251,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp)
as.freq = s->freq;
as.nchannels = 2;
- as.fmt = AUD_FMT_S16;
+ as.fmt = AUDIO_FORMAT_S16;
as.endianness = GUS_ENDIANNESS;
s->voice = AUD_open_out (
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 617a1c1016..c25bfa38b1 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -99,9 +99,9 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
}
switch (format & AC_FMT_BITS_MASK) {
- case AC_FMT_BITS_8: as->fmt = AUD_FMT_S8; break;
- case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break;
- case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break;
+ case AC_FMT_BITS_8: as->fmt = AUDIO_FORMAT_S8; break;
+ case AC_FMT_BITS_16: as->fmt = AUDIO_FORMAT_S16; break;
+ case AC_FMT_BITS_32: as->fmt = AUDIO_FORMAT_S32; break;
}
as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
@@ -134,12 +134,12 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
/* -------------------------------------------------------------------------- */
static const char *fmt2name[] = {
- [ AUD_FMT_U8 ] = "PCM-U8",
- [ AUD_FMT_S8 ] = "PCM-S8",
- [ AUD_FMT_U16 ] = "PCM-U16",
- [ AUD_FMT_S16 ] = "PCM-S16",
- [ AUD_FMT_U32 ] = "PCM-U32",
- [ AUD_FMT_S32 ] = "PCM-S32",
+ [ AUDIO_FORMAT_U8 ] = "PCM-U8",
+ [ AUDIO_FORMAT_S8 ] = "PCM-S8",
+ [ AUDIO_FORMAT_U16 ] = "PCM-U16",
+ [ AUDIO_FORMAT_S16 ] = "PCM-S16",
+ [ AUDIO_FORMAT_U32 ] = "PCM-U32",
+ [ AUDIO_FORMAT_S32 ] = "PCM-S32",
};
typedef struct HDAAudioState HDAAudioState;
diff --git a/hw/audio/lm4549.c b/hw/audio/lm4549.c
index a46f2301af..af8b22b541 100644
--- a/hw/audio/lm4549.c
+++ b/hw/audio/lm4549.c
@@ -185,7 +185,7 @@ void lm4549_write(lm4549_state *s,
struct audsettings as;
as.freq = value;
as.nchannels = 2;
- as.fmt = AUD_FMT_S16;
+ as.fmt = AUDIO_FORMAT_S16;
as.endianness = 0;
s->voice = AUD_open_out(
@@ -255,7 +255,7 @@ static int lm4549_post_load(void *opaque, int version_id)
struct audsettings as;
as.freq = freq;
as.nchannels = 2;
- as.fmt = AUD_FMT_S16;
+ as.fmt = AUDIO_FORMAT_S16;
as.endianness = 0;
s->voice = AUD_open_out(
@@ -292,7 +292,7 @@ void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque)
/* Open a default voice */
as.freq = 48000;
as.nchannels = 2;
- as.fmt = AUD_FMT_S16;
+ as.fmt = AUDIO_FORMAT_S16;
as.endianness = 0;
s->voice = AUD_open_out(
diff --git a/hw/audio/milkymist-ac97.c b/hw/audio/milkymist-ac97.c
index bc8db71ae0..90cce1e6ed 100644
--- a/hw/audio/milkymist-ac97.c
+++ b/hw/audio/milkymist-ac97.c
@@ -308,7 +308,7 @@ static void milkymist_ac97_realize(DeviceState *dev, Error **errp)
as.freq = 48000;
as.nchannels = 2;
- as.fmt = AUD_FMT_S16;
+ as.fmt = AUDIO_FORMAT_S16;
as.endianness = 1;
s->voice_in = AUD_open_in(&s->card, s->voice_in,
diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c
index b80a62ce90..fdbb4b6e99 100644
--- a/hw/audio/pcspk.c
+++ b/hw/audio/pcspk.c
@@ -162,7 +162,7 @@ static void pcspk_initfn(Object *obj)
static void pcspk_realizefn(DeviceState *dev, Error **errp)
{
- struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0};
+ struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUDIO_FORMAT_U8, 0};
ISADevice *isadev = ISA_DEVICE(dev);
PCSpkState *s = PC_SPEAKER(dev);
diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c
index c5b9bf79e8..65ea0cd938 100644
--- a/hw/audio/sb16.c
+++ b/hw/audio/sb16.c
@@ -66,7 +66,7 @@ typedef struct SB16State {
int fmt_stereo;
int fmt_signed;
int fmt_bits;
- audfmt_e fmt;
+ AudioFormat fmt;
int dma_auto;
int block_size;
int fifo;
@@ -224,7 +224,7 @@ static void continue_dma8 (SB16State *s)
static void dma_cmd8 (SB16State *s, int mask, int dma_len)
{
- s->fmt = AUD_FMT_U8;
+ s->fmt = AUDIO_FORMAT_U8;
s->use_hdma = 0;
s->fmt_bits = 8;
s->fmt_signed = 0;
@@ -319,18 +319,18 @@ static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
if (16 == s->fmt_bits) {
if (s->fmt_signed) {
- s->fmt = AUD_FMT_S16;
+ s->fmt = AUDIO_FORMAT_S16;
}
else {
- s->fmt = AUD_FMT_U16;
+ s->fmt = AUDIO_FORMAT_U16;
}
}
else {
if (s->fmt_signed) {
- s->fmt = AUD_FMT_S8;
+ s->fmt = AUDIO_FORMAT_S8;
}
else {
- s->fmt = AUD_FMT_U8;
+ s->fmt = AUDIO_FORMAT_U8;
}
}
@@ -852,7 +852,7 @@ static void legacy_reset (SB16State *s)
as.freq = s->freq;
as.nchannels = 1;
- as.fmt = AUD_FMT_U8;
+ as.fmt = AUDIO_FORMAT_U8;
as.endianness = 0;
s->voice = AUD_open_out (
diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c
index 169b006ade..ca0ad73caf 100644
--- a/hw/audio/wm8750.c
+++ b/hw/audio/wm8750.c
@@ -201,7 +201,7 @@ static void wm8750_set_format(WM8750State *s)
in_fmt.endianness = 0;
in_fmt.nchannels = 2;
in_fmt.freq = s->adc_hz;
- in_fmt.fmt = AUD_FMT_S16;
+ in_fmt.fmt = AUDIO_FORMAT_S16;
s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0],
CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt);
@@ -214,7 +214,7 @@ static void wm8750_set_format(WM8750State *s)
out_fmt.endianness = 0;
out_fmt.nchannels = 2;
out_fmt.freq = s->dac_hz;
- out_fmt.fmt = AUD_FMT_S16;
+ out_fmt.fmt = AUDIO_FORMAT_S16;
s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt);
@@ -681,7 +681,7 @@ uint32_t wm8750_adc_dat(void *opaque)
if (s->idx_in >= sizeof(s->data_in)) {
wm8750_in_load(s);
if (s->idx_in >= sizeof(s->data_in)) {
- return 0x80008000; /* silence in AUD_FMT_S16 sample format */
+ return 0x80008000; /* silence in AUDIO_FORMAT_S16 sample format */
}
}
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index 8325b5e88a..7caf92532a 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -324,8 +324,8 @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
const uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds;
uint64_t slba = le64_to_cpu(rw->slba);
uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
- uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
- uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
+ uint64_t offset = slba << data_shift;
+ uint32_t count = nlb << data_shift;
if (unlikely(slba + nlb > ns->id_ns.nsze)) {
trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
@@ -335,7 +335,7 @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
req->has_sg = false;
block_acct_start(blk_get_stats(n->conf.blk), &req->acct, 0,
BLOCK_ACCT_WRITE);
- req->aiocb = blk_aio_pwrite_zeroes(n->conf.blk, aio_slba, aio_nlb,
+ req->aiocb = blk_aio_pwrite_zeroes(n->conf.blk, offset, count,
BDRV_REQ_MAY_UNMAP, nvme_rw_cb, req);
return NVME_NO_COMPLETE;
}
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 766ca5899d..743fef2898 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -22,6 +22,7 @@
#include "qemu/error-report.h"
#include "sysemu/qtest.h"
#include "hw/pci/pci.h"
+#include "hw/mem/nvdimm.h"
GlobalProperty hw_compat_3_1[] = {
{ "pcie-root-port", "x-speed", "2_5" },
@@ -481,6 +482,47 @@ static void machine_set_memory_encryption(Object *obj, const char *value,
ms->memory_encryption = g_strdup(value);
}
+static bool machine_get_nvdimm(Object *obj, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ return ms->nvdimms_state->is_enabled;
+}
+
+static void machine_set_nvdimm(Object *obj, bool value, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ ms->nvdimms_state->is_enabled = value;
+}
+
+static char *machine_get_nvdimm_persistence(Object *obj, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ return g_strdup(ms->nvdimms_state->persistence_string);
+}
+
+static void machine_set_nvdimm_persistence(Object *obj, const char *value,
+ Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+ NVDIMMState *nvdimms_state = ms->nvdimms_state;
+
+ if (strcmp(value, "cpu") == 0) {
+ nvdimms_state->persistence = 3;
+ } else if (strcmp(value, "mem-ctrl") == 0) {
+ nvdimms_state->persistence = 2;
+ } else {
+ error_setg(errp, "-machine nvdimm-persistence=%s: unsupported option",
+ value);
+ return;
+ }
+
+ g_free(nvdimms_state->persistence_string);
+ nvdimms_state->persistence_string = g_strdup(value);
+}
+
void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type)
{
strList *item = g_new0(strList, 1);
@@ -791,6 +833,28 @@ static void machine_initfn(Object *obj)
ms->mem_merge = true;
ms->enable_graphics = true;
+ if (mc->nvdimm_supported) {
+ Object *obj = OBJECT(ms);
+
+ ms->nvdimms_state = g_new0(NVDIMMState, 1);
+ object_property_add_bool(obj, "nvdimm",
+ machine_get_nvdimm, machine_set_nvdimm,
+ &error_abort);
+ object_property_set_description(obj, "nvdimm",
+ "Set on/off to enable/disable "
+ "NVDIMM instantiation", NULL);
+
+ object_property_add_str(obj, "nvdimm-persistence",
+ machine_get_nvdimm_persistence,
+ machine_set_nvdimm_persistence,
+ &error_abort);
+ object_property_set_description(obj, "nvdimm-persistence",
+ "Set NVDIMM persistence"
+ "Valid values are cpu, mem-ctrl",
+ NULL);
+ }
+
+
/* Register notifier when init is done for sysbus sanity checks */
ms->sysbus_notifier.notify = machine_init_notify;
qemu_add_machine_init_done_notifier(&ms->sysbus_notifier);
@@ -809,6 +873,7 @@ static void machine_finalize(Object *obj)
g_free(ms->dt_compatible);
g_free(ms->firmware);
g_free(ms->device_memory);
+ g_free(ms->nvdimms_state);
}
bool machine_usb(MachineState *machine)
diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c
index cc0f9bc9cc..11b09bd18c 100644
--- a/hw/display/xlnx_dp.c
+++ b/hw/display/xlnx_dp.c
@@ -1260,7 +1260,7 @@ static void xlnx_dp_realize(DeviceState *dev, Error **errp)
as.freq = 44100;
as.nchannels = 2;
- as.fmt = AUD_FMT_S16;
+ as.fmt = AUDIO_FORMAT_S16;
as.endianness = 0;
AUD_register_card("xlnx_dp.audio", &s->aud_card);
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 9ecc96dcc7..416da318ae 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -1867,7 +1867,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
aml_append(scope, method);
}
- if (pcms->acpi_nvdimm_state.is_enabled) {
+ if (machine->nvdimms_state->is_enabled) {
method = aml_method("_E04", 0, AML_NOTSERIALIZED);
aml_append(method, aml_notify(aml_name("\\_SB.NVDR"),
aml_int(0x80)));
@@ -2704,9 +2704,9 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
build_dmar_q35(tables_blob, tables->linker);
}
}
- if (pcms->acpi_nvdimm_state.is_enabled) {
+ if (machine->nvdimms_state->is_enabled) {
nvdimm_build_acpi(table_offsets, tables_blob, tables->linker,
- &pcms->acpi_nvdimm_state, machine->ram_slots);
+ machine->nvdimms_state, machine->ram_slots);
}
/* Add tables supplied by user (if any) */
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index d71dc28ef6..1cdaff5f4d 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -2075,6 +2075,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
{
const PCMachineState *pcms = PC_MACHINE(hotplug_dev);
const PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+ const MachineState *ms = MACHINE(hotplug_dev);
const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
const uint64_t legacy_align = TARGET_PAGE_SIZE;
@@ -2089,7 +2090,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
return;
}
- if (is_nvdimm && !pcms->acpi_nvdimm_state.is_enabled) {
+ if (is_nvdimm && !ms->nvdimms_state->is_enabled) {
error_setg(errp, "nvdimm is not enabled: missing 'nvdimm' in '-M'");
return;
}
@@ -2103,6 +2104,7 @@ static void pc_memory_plug(HotplugHandler *hotplug_dev,
{
Error *local_err = NULL;
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
+ MachineState *ms = MACHINE(hotplug_dev);
bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
pc_dimm_plug(PC_DIMM(dev), MACHINE(pcms), &local_err);
@@ -2111,7 +2113,7 @@ static void pc_memory_plug(HotplugHandler *hotplug_dev,
}
if (is_nvdimm) {
- nvdimm_plug(&pcms->acpi_nvdimm_state);
+ nvdimm_plug(ms->nvdimms_state);
}
hotplug_handler_plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &error_abort);
@@ -2552,47 +2554,6 @@ static void pc_machine_set_smm(Object *obj, Visitor *v, const char *name,
visit_type_OnOffAuto(v, name, &pcms->smm, errp);
}
-static bool pc_machine_get_nvdimm(Object *obj, Error **errp)
-{
- PCMachineState *pcms = PC_MACHINE(obj);
-
- return pcms->acpi_nvdimm_state.is_enabled;
-}
-
-static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp)
-{
- PCMachineState *pcms = PC_MACHINE(obj);
-
- pcms->acpi_nvdimm_state.is_enabled = value;
-}
-
-static char *pc_machine_get_nvdimm_persistence(Object *obj, Error **errp)
-{
- PCMachineState *pcms = PC_MACHINE(obj);
-
- return g_strdup(pcms->acpi_nvdimm_state.persistence_string);
-}
-
-static void pc_machine_set_nvdimm_persistence(Object *obj, const char *value,
- Error **errp)
-{
- PCMachineState *pcms = PC_MACHINE(obj);
- AcpiNVDIMMState *nvdimm_state = &pcms->acpi_nvdimm_state;
-
- if (strcmp(value, "cpu") == 0)
- nvdimm_state->persistence = 3;
- else if (strcmp(value, "mem-ctrl") == 0)
- nvdimm_state->persistence = 2;
- else {
- error_setg(errp, "-machine nvdimm-persistence=%s: unsupported option",
- value);
- return;
- }
-
- g_free(nvdimm_state->persistence_string);
- nvdimm_state->persistence_string = g_strdup(value);
-}
-
static bool pc_machine_get_smbus(Object *obj, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
@@ -2642,8 +2603,6 @@ static void pc_machine_initfn(Object *obj)
pcms->max_ram_below_4g = 0; /* use default */
pcms->smm = ON_OFF_AUTO_AUTO;
pcms->vmport = ON_OFF_AUTO_AUTO;
- /* nvdimm is disabled on default. */
- pcms->acpi_nvdimm_state.is_enabled = false;
/* acpi build is enabled by default if machine supports it */
pcms->acpi_build_enabled = PC_MACHINE_GET_CLASS(pcms)->has_acpi_build;
pcms->smbus_enabled = true;
@@ -2782,6 +2741,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
hc->unplug = pc_machine_device_unplug_cb;
nc->nmi_monitor_handler = x86_nmi;
mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE;
+ mc->nvdimm_supported = true;
object_class_property_add(oc, PC_MACHINE_DEVMEM_REGION_SIZE, "int",
pc_machine_get_device_memory_region_size, NULL,
@@ -2806,13 +2766,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
object_class_property_set_description(oc, PC_MACHINE_VMPORT,
"Enable vmport (pc & q35)", &error_abort);
- object_class_property_add_bool(oc, PC_MACHINE_NVDIMM,
- pc_machine_get_nvdimm, pc_machine_set_nvdimm, &error_abort);
-
- object_class_property_add_str(oc, PC_MACHINE_NVDIMM_PERSIST,
- pc_machine_get_nvdimm_persistence,
- pc_machine_set_nvdimm_persistence, &error_abort);
-
object_class_property_add_bool(oc, PC_MACHINE_SMBUS,
pc_machine_get_smbus, pc_machine_set_smbus, &error_abort);
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 8770ecada9..8ad8e885c6 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -297,8 +297,8 @@ static void pc_init1(MachineState *machine,
PC_MACHINE_ACPI_DEVICE_PROP, &error_abort);
}
- if (pcms->acpi_nvdimm_state.is_enabled) {
- nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
+ if (machine->nvdimms_state->is_enabled) {
+ nvdimm_init_acpi_state(machine->nvdimms_state, system_io,
pcms->fw_cfg, OBJECT(pcms));
}
}
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index cfb9043e12..372c6b73be 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -329,8 +329,8 @@ static void pc_q35_init(MachineState *machine)
pc_vga_init(isa_bus, host_bus);
pc_nic_init(pcmc, isa_bus, host_bus);
- if (pcms->acpi_nvdimm_state.is_enabled) {
- nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
+ if (machine->nvdimms_state->is_enabled) {
+ nvdimm_init_acpi_state(machine->nvdimms_state, system_io,
pcms->fw_cfg, OBJECT(pcms));
}
}
diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c
index 2eb3cb9518..41731619bb 100644
--- a/hw/input/tsc210x.c
+++ b/hw/input/tsc210x.c
@@ -318,7 +318,7 @@ static void tsc2102_audio_output_update(TSC210xState *s)
fmt.endianness = 0;
fmt.nchannels = 2;
fmt.freq = s->codec.tx_rate;
- fmt.fmt = AUD_FMT_S16;
+ fmt.fmt = AUDIO_FORMAT_S16;
s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
"tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt);
diff --git a/hw/sd/Kconfig b/hw/sd/Kconfig
index 864f535011..c5e1e5581c 100644
--- a/hw/sd/Kconfig
+++ b/hw/sd/Kconfig
@@ -12,6 +12,10 @@ config SD
config SDHCI
bool
+ select SD
+
+config SDHCI_PCI
+ bool
default y if PCI_DEVICES
depends on PCI
- select SD
+ select SDHCI
diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs
index a99d9fbb04..06657279d1 100644
--- a/hw/sd/Makefile.objs
+++ b/hw/sd/Makefile.objs
@@ -2,6 +2,7 @@ common-obj-$(CONFIG_PL181) += pl181.o
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
common-obj-$(CONFIG_SD) += sd.o core.o sdmmc-internal.o
common-obj-$(CONFIG_SDHCI) += sdhci.o
+common-obj-$(CONFIG_SDHCI_PCI) += sdhci-pci.o
obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
obj-$(CONFIG_OMAP) += omap_mmc.o
diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h
index 19665fd401..34141400f8 100644
--- a/hw/sd/sdhci-internal.h
+++ b/hw/sd/sdhci-internal.h
@@ -304,4 +304,38 @@ extern const VMStateDescription sdhci_vmstate;
#define ESDHC_PRNSTS_SDSTB (1 << 3)
+/*
+ * Default SD/MMC host controller features information, which will be
+ * presented in CAPABILITIES register of generic SD host controller at reset.
+ *
+ * support:
+ * - 3.3v and 1.8v voltages
+ * - SDMA/ADMA1/ADMA2
+ * - high-speed
+ * max host controller R/W buffers size: 512B
+ * max clock frequency for SDclock: 52 MHz
+ * timeout clock frequency: 52 MHz
+ *
+ * does not support:
+ * - 3.0v voltage
+ * - 64-bit system bus
+ * - suspend/resume
+ */
+#define SDHC_CAPAB_REG_DEFAULT 0x057834b4
+
+#define DEFINE_SDHCI_COMMON_PROPERTIES(_state) \
+ DEFINE_PROP_UINT8("sd-spec-version", _state, sd_spec_version, 2), \
+ DEFINE_PROP_UINT8("uhs", _state, uhs_mode, UHS_NOT_SUPPORTED), \
+ \
+ /* Capabilities registers provide information on supported
+ * features of this specific host controller implementation */ \
+ DEFINE_PROP_UINT64("capareg", _state, capareg, SDHC_CAPAB_REG_DEFAULT), \
+ DEFINE_PROP_UINT64("maxcurr", _state, maxcurr, 0)
+
+void sdhci_initfn(SDHCIState *s);
+void sdhci_uninitfn(SDHCIState *s);
+void sdhci_common_realize(SDHCIState *s, Error **errp);
+void sdhci_common_unrealize(SDHCIState *s, Error **errp);
+void sdhci_common_class_init(ObjectClass *klass, void *data);
+
#endif
diff --git a/hw/sd/sdhci-pci.c b/hw/sd/sdhci-pci.c
new file mode 100644
index 0000000000..f884661862
--- /dev/null
+++ b/hw/sd/sdhci-pci.c
@@ -0,0 +1,87 @@
+/*
+ * SDHCI device on PCI
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/sd/sdhci.h"
+#include "sdhci-internal.h"
+
+static Property sdhci_pci_properties[] = {
+ DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sdhci_pci_realize(PCIDevice *dev, Error **errp)
+{
+ SDHCIState *s = PCI_SDHCI(dev);
+ Error *local_err = NULL;
+
+ sdhci_initfn(s);
+ sdhci_common_realize(s, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ dev->config[PCI_CLASS_PROG] = 0x01; /* Standard Host supported DMA */
+ dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
+ s->irq = pci_allocate_irq(dev);
+ s->dma_as = pci_get_address_space(dev);
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->iomem);
+}
+
+static void sdhci_pci_exit(PCIDevice *dev)
+{
+ SDHCIState *s = PCI_SDHCI(dev);
+
+ sdhci_common_unrealize(s, &error_abort);
+ sdhci_uninitfn(s);
+}
+
+static void sdhci_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = sdhci_pci_realize;
+ k->exit = sdhci_pci_exit;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_SDHCI;
+ k->class_id = PCI_CLASS_SYSTEM_SDHCI;
+ dc->props = sdhci_pci_properties;
+
+ sdhci_common_class_init(klass, data);
+}
+
+static const TypeInfo sdhci_pci_info = {
+ .name = TYPE_PCI_SDHCI,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(SDHCIState),
+ .class_init = sdhci_pci_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void sdhci_pci_register_type(void)
+{
+ type_register_static(&sdhci_pci_info);
+}
+
+type_init(sdhci_pci_register_type)
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index 83f1574ffd..17ad5465a7 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -40,24 +40,6 @@
#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
-/* Default SD/MMC host controller features information, which will be
- * presented in CAPABILITIES register of generic SD host controller at reset.
- *
- * support:
- * - 3.3v and 1.8v voltages
- * - SDMA/ADMA1/ADMA2
- * - high-speed
- * max host controller R/W buffers size: 512B
- * max clock frequency for SDclock: 52 MHz
- * timeout clock frequency: 52 MHz
- *
- * does not support:
- * - 3.0v voltage
- * - 64-bit system bus
- * - suspend/resume
- */
-#define SDHC_CAPAB_REG_DEFAULT 0x057834b4
-
static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
{
return 1 << (9 + FIELD_EX32(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH));
@@ -1328,16 +1310,7 @@ static void sdhci_init_readonly_registers(SDHCIState *s, Error **errp)
/* --- qdev common --- */
-#define DEFINE_SDHCI_COMMON_PROPERTIES(_state) \
- DEFINE_PROP_UINT8("sd-spec-version", _state, sd_spec_version, 2), \
- DEFINE_PROP_UINT8("uhs", _state, uhs_mode, UHS_NOT_SUPPORTED), \
- \
- /* Capabilities registers provide information on supported
- * features of this specific host controller implementation */ \
- DEFINE_PROP_UINT64("capareg", _state, capareg, SDHC_CAPAB_REG_DEFAULT), \
- DEFINE_PROP_UINT64("maxcurr", _state, maxcurr, 0)
-
-static void sdhci_initfn(SDHCIState *s)
+void sdhci_initfn(SDHCIState *s)
{
qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
TYPE_SDHCI_BUS, DEVICE(s), "sd-bus");
@@ -1348,7 +1321,7 @@ static void sdhci_initfn(SDHCIState *s)
s->io_ops = &sdhci_mmio_ops;
}
-static void sdhci_uninitfn(SDHCIState *s)
+void sdhci_uninitfn(SDHCIState *s)
{
timer_del(s->insert_timer);
timer_free(s->insert_timer);
@@ -1359,7 +1332,7 @@ static void sdhci_uninitfn(SDHCIState *s)
s->fifo_buffer = NULL;
}
-static void sdhci_common_realize(SDHCIState *s, Error **errp)
+void sdhci_common_realize(SDHCIState *s, Error **errp)
{
Error *local_err = NULL;
@@ -1375,7 +1348,7 @@ static void sdhci_common_realize(SDHCIState *s, Error **errp)
SDHC_REGISTERS_MAP_SIZE);
}
-static void sdhci_common_unrealize(SDHCIState *s, Error **errp)
+void sdhci_common_unrealize(SDHCIState *s, Error **errp)
{
/* This function is expected to be called only once for each class:
* - SysBus: via DeviceClass->unrealize(),
@@ -1445,7 +1418,7 @@ const VMStateDescription sdhci_vmstate = {
},
};
-static void sdhci_common_class_init(ObjectClass *klass, void *data)
+void sdhci_common_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1454,66 +1427,6 @@ static void sdhci_common_class_init(ObjectClass *klass, void *data)
dc->reset = sdhci_poweron_reset;
}
-/* --- qdev PCI --- */
-
-static Property sdhci_pci_properties[] = {
- DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void sdhci_pci_realize(PCIDevice *dev, Error **errp)
-{
- SDHCIState *s = PCI_SDHCI(dev);
- Error *local_err = NULL;
-
- sdhci_initfn(s);
- sdhci_common_realize(s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- dev->config[PCI_CLASS_PROG] = 0x01; /* Standard Host supported DMA */
- dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
- s->irq = pci_allocate_irq(dev);
- s->dma_as = pci_get_address_space(dev);
- pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->iomem);
-}
-
-static void sdhci_pci_exit(PCIDevice *dev)
-{
- SDHCIState *s = PCI_SDHCI(dev);
-
- sdhci_common_unrealize(s, &error_abort);
- sdhci_uninitfn(s);
-}
-
-static void sdhci_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
- k->realize = sdhci_pci_realize;
- k->exit = sdhci_pci_exit;
- k->vendor_id = PCI_VENDOR_ID_REDHAT;
- k->device_id = PCI_DEVICE_ID_REDHAT_SDHCI;
- k->class_id = PCI_CLASS_SYSTEM_SDHCI;
- dc->props = sdhci_pci_properties;
-
- sdhci_common_class_init(klass, data);
-}
-
-static const TypeInfo sdhci_pci_info = {
- .name = TYPE_PCI_SDHCI,
- .parent = TYPE_PCI_DEVICE,
- .instance_size = sizeof(SDHCIState),
- .class_init = sdhci_pci_class_init,
- .interfaces = (InterfaceInfo[]) {
- { INTERFACE_CONVENTIONAL_PCI_DEVICE },
- { },
- },
-};
-
/* --- qdev SysBus --- */
static Property sdhci_sysbus_properties[] = {
@@ -1846,7 +1759,6 @@ static const TypeInfo imx_usdhc_info = {
static void sdhci_register_types(void)
{
- type_register_static(&sdhci_pci_info);
type_register_static(&sdhci_sysbus_info);
type_register_static(&sdhci_bus_info);
type_register_static(&imx_usdhc_info);
diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
index 28ac7c5165..c46d5eeb79 100644
--- a/hw/usb/dev-audio.c
+++ b/hw/usb/dev-audio.c
@@ -650,7 +650,7 @@ static void usb_audio_realize(USBDevice *dev, Error **errp)
s->out.vol[1] = 240; /* 0 dB */
s->out.as.freq = USBAUDIO_SAMPLE_RATE;
s->out.as.nchannels = 2;
- s->out.as.fmt = AUD_FMT_S16;
+ s->out.as.fmt = AUDIO_FORMAT_S16;
s->out.as.endianness = 0;
streambuf_init(&s->out.buf, s->buffer);
diff --git a/include/block/block.h b/include/block/block.h
index 6a758a76f8..e452988b66 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -187,6 +187,9 @@ typedef struct BDRVReopenState {
BlockDriverState *bs;
int flags;
BlockdevDetectZeroesOptions detect_zeroes;
+ bool backing_missing;
+ bool replace_backing_bs; /* new_backing_bs is ignored if this is false */
+ BlockDriverState *new_backing_bs; /* If NULL then detach the current bs */
uint64_t perm, shared_perm;
QDict *options;
QDict *explicit_options;
@@ -299,8 +302,9 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
int flags, Error **errp);
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
- BlockDriverState *bs, QDict *options);
-int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp);
+ BlockDriverState *bs, QDict *options,
+ bool keep_old_opts);
+int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
Error **errp);
int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
@@ -353,6 +357,11 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
BlockDriverState *bdrv_find_overlay(BlockDriverState *active,
BlockDriverState *bs);
BlockDriverState *bdrv_find_base(BlockDriverState *bs);
+bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base,
+ Error **errp);
+int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base,
+ Error **errp);
+void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base);
typedef struct BdrvCheckResult {
diff --git a/include/block/block_int.h b/include/block/block_int.h
index a23cabaddd..01e855a066 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -383,6 +383,14 @@ struct BlockDriver {
/* List of options for creating images, terminated by name == NULL */
QemuOptsList *create_opts;
+ /*
+ * If this driver supports reopening images this contains a
+ * NULL-terminated list of the runtime options that can be
+ * modified. If an option in this list is unspecified during
+ * reopen then it _must_ be reset to its default value or return
+ * an error.
+ */
+ const char *const *mutable_opts;
/*
* Returns 0 for completed check, -errno for internal errors.
@@ -711,6 +719,12 @@ struct BdrvChild {
uint64_t backup_perm;
uint64_t backup_shared_perm;
+ /*
+ * This link is frozen: the child can neither be replaced nor
+ * detached from the parent.
+ */
+ bool frozen;
+
QLIST_ENTRY(BdrvChild) next;
QLIST_ENTRY(BdrvChild) next_parent;
};
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 9690c71a6d..e231860666 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -210,6 +210,7 @@ struct MachineClass {
int nb_nodes, ram_addr_t size);
bool ignore_boot_device_suffixes;
bool smbus_no_migration_support;
+ bool nvdimm_supported;
HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
DeviceState *dev);
@@ -272,6 +273,7 @@ struct MachineState {
const char *cpu_type;
AccelState *accelerator;
CPUArchIdList *possible_cpus;
+ struct NVDIMMState *nvdimms_state;
};
#define DEFINE_MACHINE(namestr, machine_initfn) \
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 276ff15d4d..ca65ef18af 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -47,8 +47,6 @@ struct PCMachineState {
OnOffAuto vmport;
OnOffAuto smm;
- AcpiNVDIMMState acpi_nvdimm_state;
-
bool acpi_build_enabled;
bool smbus_enabled;
bool sata_enabled;
@@ -76,8 +74,6 @@ struct PCMachineState {
#define PC_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g"
#define PC_MACHINE_VMPORT "vmport"
#define PC_MACHINE_SMM "smm"
-#define PC_MACHINE_NVDIMM "nvdimm"
-#define PC_MACHINE_NVDIMM_PERSIST "nvdimm-persistence"
#define PC_MACHINE_SMBUS "smbus"
#define PC_MACHINE_SATA "sata"
#define PC_MACHINE_PIT "pit"
diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h
index c5c9b3c7f8..523a9b3d4a 100644
--- a/include/hw/mem/nvdimm.h
+++ b/include/hw/mem/nvdimm.h
@@ -123,7 +123,7 @@ struct NvdimmFitBuffer {
};
typedef struct NvdimmFitBuffer NvdimmFitBuffer;
-struct AcpiNVDIMMState {
+struct NVDIMMState {
/* detect if NVDIMM support is enabled. */
bool is_enabled;
@@ -141,13 +141,13 @@ struct AcpiNVDIMMState {
int32_t persistence;
char *persistence_string;
};
-typedef struct AcpiNVDIMMState AcpiNVDIMMState;
+typedef struct NVDIMMState NVDIMMState;
-void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io,
+void nvdimm_init_acpi_state(NVDIMMState *state, MemoryRegion *io,
FWCfgState *fw_cfg, Object *owner);
void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data,
- BIOSLinker *linker, AcpiNVDIMMState *state,
+ BIOSLinker *linker, NVDIMMState *state,
uint32_t ram_slots);
-void nvdimm_plug(AcpiNVDIMMState *state);
+void nvdimm_plug(NVDIMMState *state);
void nvdimm_acpi_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev);
#endif
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index 840af09cb0..303d315c5d 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -571,6 +571,19 @@ void os_mem_prealloc(int fd, char *area, size_t sz, int smp_cpus,
Error **errp);
/**
+ * qemu_get_pmem_size:
+ * @filename: path to a pmem file
+ * @errp: pointer to a NULL-initialized error object
+ *
+ * Determine the size of a persistent memory file. Besides supporting files on
+ * DAX file systems, this function also supports Linux devdax character
+ * devices.
+ *
+ * Returns: the size or 0 on failure
+ */
+uint64_t qemu_get_pmem_size(const char *filename, Error **errp);
+
+/**
* qemu_get_pid_name:
* @pid: pid of a process
*
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 77acca0209..729e5185c5 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -5,9 +5,9 @@ util-obj-y += opts-visitor.o qapi-clone-visitor.o
util-obj-y += qmp-event.o
util-obj-y += qapi-util.o
-QAPI_COMMON_MODULES = authz block-core block char common crypto introspect
-QAPI_COMMON_MODULES += job migration misc net rdma rocker run-state
-QAPI_COMMON_MODULES += sockets tpm trace transaction ui
+QAPI_COMMON_MODULES = audio authz block-core block char common crypto
+QAPI_COMMON_MODULES += introspect job migration misc net rdma rocker
+QAPI_COMMON_MODULES += run-state sockets tpm trace transaction ui
QAPI_TARGET_MODULES = target
QAPI_MODULES = $(QAPI_COMMON_MODULES) $(QAPI_TARGET_MODULES)
diff --git a/qapi/audio.json b/qapi/audio.json
new file mode 100644
index 0000000000..97aee37288
--- /dev/null
+++ b/qapi/audio.json
@@ -0,0 +1,304 @@
+# -*- mode: python -*-
+#
+# Copyright (C) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+
+##
+# @AudiodevPerDirectionOptions:
+#
+# General audio backend options that are used for both playback and
+# recording.
+#
+# @fixed-settings: use fixed settings for host input/output. When off,
+# frequency, channels and format must not be
+# specified (default true)
+#
+# @frequency: frequency to use when using fixed settings
+# (default 44100)
+#
+# @channels: number of channels when using fixed settings (default 2)
+#
+# @voices: number of voices to use (default 1)
+#
+# @format: sample format to use when using fixed settings
+# (default s16)
+#
+# @buffer-length: the buffer length in microseconds
+#
+# Since: 4.0
+##
+{ 'struct': 'AudiodevPerDirectionOptions',
+ 'data': {
+ '*fixed-settings': 'bool',
+ '*frequency': 'uint32',
+ '*channels': 'uint32',
+ '*voices': 'uint32',
+ '*format': 'AudioFormat',
+ '*buffer-length': 'uint32' } }
+
+##
+# @AudiodevGenericOptions:
+#
+# Generic driver-specific options.
+#
+# @in: options of the capture stream
+#
+# @out: options of the playback stream
+#
+# Since: 4.0
+##
+{ 'struct': 'AudiodevGenericOptions',
+ 'data': {
+ '*in': 'AudiodevPerDirectionOptions',
+ '*out': 'AudiodevPerDirectionOptions' } }
+
+##
+# @AudiodevAlsaPerDirectionOptions:
+#
+# Options of the ALSA backend that are used for both playback and
+# recording.
+#
+# @dev: the name of the ALSA device to use (default 'default')
+#
+# @period-length: the period length in microseconds
+#
+# @try-poll: attempt to use poll mode, falling back to non-polling
+# access on failure (default true)
+#
+# Since: 4.0
+##
+{ 'struct': 'AudiodevAlsaPerDirectionOptions',
+ 'base': 'AudiodevPerDirectionOptions',
+ 'data': {
+ '*dev': 'str',
+ '*period-length': 'uint32',
+ '*try-poll': 'bool' } }
+
+##
+# @AudiodevAlsaOptions:
+#
+# Options of the ALSA audio backend.
+#
+# @in: options of the capture stream
+#
+# @out: options of the playback stream
+#
+# @threshold: set the threshold (in microseconds) when playback starts
+#
+# Since: 4.0
+##
+{ 'struct': 'AudiodevAlsaOptions',
+ 'data': {
+ '*in': 'AudiodevAlsaPerDirectionOptions',
+ '*out': 'AudiodevAlsaPerDirectionOptions',
+ '*threshold': 'uint32' } }
+
+##
+# @AudiodevCoreaudioPerDirectionOptions:
+#
+# Options of the Core Audio backend that are used for both playback and
+# recording.
+#
+# @buffer-count: number of buffers
+#
+# Since: 4.0
+##
+{ 'struct': 'AudiodevCoreaudioPerDirectionOptions',
+ 'base': 'AudiodevPerDirectionOptions',
+ 'data': {
+ '*buffer-count': 'uint32' } }
+
+##
+# @AudiodevCoreaudioOptions:
+#
+# Options of the coreaudio audio backend.
+#
+# @in: options of the capture stream
+#
+# @out: options of the playback stream
+#
+# Since: 4.0
+##
+{ 'struct': 'AudiodevCoreaudioOptions',
+ 'data': {
+ '*in': 'AudiodevCoreaudioPerDirectionOptions',
+ '*out': 'AudiodevCoreaudioPerDirectionOptions' } }
+
+##
+# @AudiodevDsoundOptions:
+#
+# Options of the DirectSound audio backend.
+#
+# @in: options of the capture stream
+#
+# @out: options of the playback stream
+#
+# @latency: add extra latency to playback in microseconds
+# (default 10000)
+#
+# Since: 4.0
+##
+{ 'struct': 'AudiodevDsoundOptions',
+ 'data': {
+ '*in': 'AudiodevPerDirectionOptions',
+ '*out': 'AudiodevPerDirectionOptions',
+ '*latency': 'uint32' } }
+
+##
+# @AudiodevOssPerDirectionOptions:
+#
+# Options of the OSS backend that are used for both playback and
+# recording.
+#
+# @dev: file name of the OSS device (default '/dev/dsp')
+#
+# @buffer-count: number of buffers
+#
+# @try-poll: attempt to use poll mode, falling back to non-polling
+# access on failure (default true)
+#
+# Since: 4.0
+##
+{ 'struct': 'AudiodevOssPerDirectionOptions',
+ 'base': 'AudiodevPerDirectionOptions',
+ 'data': {
+ '*dev': 'str',
+ '*buffer-count': 'uint32',
+ '*try-poll': 'bool' } }
+
+##
+# @AudiodevOssOptions:
+#
+# Options of the OSS audio backend.
+#
+# @in: options of the capture stream
+#
+# @out: options of the playback stream
+#
+# @try-mmap: try using memory-mapped access, falling back to
+# non-memory-mapped access on failure (default true)
+#
+# @exclusive: open device in exclusive mode (vmix won't work)
+# (default false)
+#
+# @dsp-policy: set the timing policy of the device (between 0 and 10,
+# where smaller number means smaller latency but higher
+# CPU usage) or -1 to use fragment mode (option ignored
+# on some platforms) (default 5)
+#
+# Since: 4.0
+##
+{ 'struct': 'AudiodevOssOptions',
+ 'data': {
+ '*in': 'AudiodevOssPerDirectionOptions',
+ '*out': 'AudiodevOssPerDirectionOptions',
+ '*try-mmap': 'bool',
+ '*exclusive': 'bool',
+ '*dsp-policy': 'uint32' } }
+
+##
+# @AudiodevPaPerDirectionOptions:
+#
+# Options of the Pulseaudio backend that are used for both playback and
+# recording.
+#
+# @name: name of the sink/source to use
+#
+# Since: 4.0
+##
+{ 'struct': 'AudiodevPaPerDirectionOptions',
+ 'base': 'AudiodevPerDirectionOptions',
+ 'data': {
+ '*name': 'str' } }
+
+##
+# @AudiodevPaOptions:
+#
+# Options of the PulseAudio audio backend.
+#
+# @in: options of the capture stream
+#
+# @out: options of the playback stream
+#
+# @server: PulseAudio server address (default: let PulseAudio choose)
+#
+# Since: 4.0
+##
+{ 'struct': 'AudiodevPaOptions',
+ 'data': {
+ '*in': 'AudiodevPaPerDirectionOptions',
+ '*out': 'AudiodevPaPerDirectionOptions',
+ '*server': 'str' } }
+
+##
+# @AudiodevWavOptions:
+#
+# Options of the wav audio backend.
+#
+# @in: options of the capture stream
+#
+# @out: options of the playback stream
+#
+# @path: name of the wav file to record (default 'qemu.wav')
+#
+# Since: 4.0
+##
+{ 'struct': 'AudiodevWavOptions',
+ 'data': {
+ '*in': 'AudiodevPerDirectionOptions',
+ '*out': 'AudiodevPerDirectionOptions',
+ '*path': 'str' } }
+
+
+##
+# @AudioFormat:
+#
+# An enumeration of possible audio formats.
+#
+# Since: 4.0
+##
+{ 'enum': 'AudioFormat',
+ 'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32' ] }
+
+##
+# @AudiodevDriver:
+#
+# An enumeration of possible audio backend drivers.
+#
+# Since: 4.0
+##
+{ 'enum': 'AudiodevDriver',
+ 'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'oss', 'pa', 'sdl',
+ 'spice', 'wav' ] }
+
+##
+# @Audiodev:
+#
+# Options of an audio backend.
+#
+# @id: identifier of the backend
+#
+# @driver: the backend driver to use
+#
+# @timer-period: timer period (in microseconds, 0: use lowest possible)
+#
+# Since: 4.0
+##
+{ 'union': 'Audiodev',
+ 'base': {
+ 'id': 'str',
+ 'driver': 'AudiodevDriver',
+ '*timer-period': 'uint32' },
+ 'discriminator': 'driver',
+ 'data': {
+ 'none': 'AudiodevGenericOptions',
+ 'alsa': 'AudiodevAlsaOptions',
+ 'coreaudio': 'AudiodevCoreaudioOptions',
+ 'dsound': 'AudiodevDsoundOptions',
+ 'oss': 'AudiodevOssOptions',
+ 'pa': 'AudiodevPaOptions',
+ 'sdl': 'AudiodevGenericOptions',
+ 'spice': 'AudiodevGenericOptions',
+ 'wav': 'AudiodevWavOptions' } }
diff --git a/qapi/block-core.json b/qapi/block-core.json
index bbb9dcc141..ca684a8a04 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -555,20 +555,20 @@
# +------------------
# 10 50 100
#
-# Since: 2.12
+# Since: 4.0
##
{ 'struct': 'BlockLatencyHistogramInfo',
'data': {'boundaries': ['uint64'], 'bins': ['uint64'] } }
##
-# @x-block-latency-histogram-set:
+# @block-latency-histogram-set:
#
# Manage read, write and flush latency histograms for the device.
#
# If only @device parameter is specified, remove all present latency histograms
# for the device. Otherwise, add/reset some of (or all) latency histograms.
#
-# @device: device name to set latency histogram for.
+# @id: The name or QOM path of the guest device.
#
# @boundaries: list of interval boundary values (see description in
# BlockLatencyHistogramInfo definition). If specified, all
@@ -591,7 +591,7 @@
#
# Returns: error if device is not found or any boundary arrays are invalid.
#
-# Since: 2.12
+# Since: 4.0
#
# Example: set new histograms for all io types with intervals
# [0, 10), [10, 50), [50, 100), [100, +inf):
@@ -625,8 +625,8 @@
# "arguments": { "device": "drive0" } }
# <- { "return": {} }
##
-{ 'command': 'x-block-latency-histogram-set',
- 'data': {'device': 'str',
+{ 'command': 'block-latency-histogram-set',
+ 'data': {'id': 'str',
'*boundaries': ['uint64'],
'*boundaries-read': ['uint64'],
'*boundaries-write': ['uint64'],
@@ -912,11 +912,11 @@
# @timed_stats: Statistics specific to the set of previously defined
# intervals of time (Since 2.5)
#
-# @x_rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
+# @rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0)
#
-# @x_wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
+# @wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0)
#
-# @x_flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
+# @flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0)
#
# Since: 0.14.0
##
@@ -931,9 +931,9 @@
'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int',
'account_invalid': 'bool', 'account_failed': 'bool',
'timed_stats': ['BlockDeviceTimedStats'],
- '*x_rd_latency_histogram': 'BlockLatencyHistogramInfo',
- '*x_wr_latency_histogram': 'BlockLatencyHistogramInfo',
- '*x_flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
+ '*rd_latency_histogram': 'BlockLatencyHistogramInfo',
+ '*wr_latency_histogram': 'BlockLatencyHistogramInfo',
+ '*flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
##
# @BlockStats:
@@ -4016,6 +4016,48 @@
{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true }
##
+# @x-blockdev-reopen:
+#
+# Reopens a block device using the given set of options. Any option
+# not specified will be reset to its default value regardless of its
+# previous status. If an option cannot be changed or a particular
+# driver does not support reopening then the command will return an
+# error.
+#
+# The top-level @node-name option (from BlockdevOptions) must be
+# specified and is used to select the block device to be reopened.
+# Other @node-name options must be either omitted or set to the
+# current name of the appropriate node. This command won't change any
+# node name and any attempt to do it will result in an error.
+#
+# In the case of options that refer to child nodes, the behavior of
+# this command depends on the value:
+#
+# 1) A set of options (BlockdevOptions): the child is reopened with
+# the specified set of options.
+#
+# 2) A reference to the current child: the child is reopened using
+# its existing set of options.
+#
+# 3) A reference to a different node: the current child is replaced
+# with the specified one.
+#
+# 4) NULL: the current child (if any) is detached.
+#
+# Options (1) and (2) are supported in all cases, but at the moment
+# only @backing allows replacing or detaching an existing child.
+#
+# Unlike with blockdev-add, the @backing option must always be present
+# unless the node being reopened does not have a backing file and its
+# image does not have a default backing file name as part of its
+# metadata.
+#
+# Since: 4.0
+##
+{ 'command': 'x-blockdev-reopen',
+ 'data': 'BlockdevOptions', 'boxed': true }
+
+##
# @blockdev-del:
#
# Deletes a block device that has been added using blockdev-add.
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
index a34899c626..4bd1223637 100644
--- a/qapi/qapi-schema.json
+++ b/qapi/qapi-schema.json
@@ -99,3 +99,4 @@
{ 'include': 'introspect.json' }
{ 'include': 'misc.json' }
{ 'include': 'target.json' }
+{ 'include': 'audio.json' }
diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi
index 63a13e6c67..2219386769 100644
--- a/qemu-deprecated.texi
+++ b/qemu-deprecated.texi
@@ -65,6 +65,13 @@ topologies described with -smp include all possible cpus, i.e.
The @code{acl} option to the @code{-vnc} argument has been replaced
by the @code{tls-authz} and @code{sasl-authz} options.
+@subsection QEMU_AUDIO_ environment variables and -audio-help (since 4.0)
+
+The ``-audiodev'' argument is now the preferred way to specify audio
+backend settings instead of environment variables. To ease migration to
+the new format, the ``-audiodev-help'' option can be used to convert
+the current values of the environment variables to ``-audiodev'' options.
+
@section QEMU Machine Protocol (QMP) commands
@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0)
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index b9f189f09b..35dcdcf413 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -2080,8 +2080,8 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
}
bdrv_subtree_drained_begin(bs);
- brq = bdrv_reopen_queue(NULL, bs, opts);
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
+ brq = bdrv_reopen_queue(NULL, bs, opts, true);
+ bdrv_reopen_multiple(brq, &local_err);
bdrv_subtree_drained_end(bs);
if (local_err) {
diff --git a/qemu-options.hx b/qemu-options.hx
index 7118d90352..8693f5fa3c 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -416,14 +416,244 @@ The default is @code{en-us}.
ETEXI
+HXCOMM Deprecated by -audiodev
DEF("audio-help", 0, QEMU_OPTION_audio_help,
- "-audio-help print list of audio drivers and their options\n",
+ "-audio-help show -audiodev equivalent of the currently specified audio settings\n",
QEMU_ARCH_ALL)
STEXI
@item -audio-help
@findex -audio-help
-Will show the audio subsystem help: list of drivers, tunable
-parameters.
+Will show the -audiodev equivalent of the currently specified
+(deprecated) environment variables.
+ETEXI
+
+DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
+ "-audiodev [driver=]driver,id=id[,prop[=value][,...]]\n"
+ " specifies the audio backend to use\n"
+ " id= identifier of the backend\n"
+ " timer-period= timer period in microseconds\n"
+ " in|out.fixed-settings= use fixed settings for host audio\n"
+ " in|out.frequency= frequency to use with fixed settings\n"
+ " in|out.channels= number of channels to use with fixed settings\n"
+ " in|out.format= sample format to use with fixed settings\n"
+ " valid values: s8, s16, s32, u8, u16, u32\n"
+ " in|out.voices= number of voices to use\n"
+ " in|out.buffer-len= length of buffer in microseconds\n"
+ "-audiodev none,id=id,[,prop[=value][,...]]\n"
+ " dummy driver that discards all output\n"
+#ifdef CONFIG_AUDIO_ALSA
+ "-audiodev alsa,id=id[,prop[=value][,...]]\n"
+ " in|out.dev= name of the audio device to use\n"
+ " in|out.period-len= length of period in microseconds\n"
+ " in|out.try-poll= attempt to use poll mode\n"
+ " threshold= threshold (in microseconds) when playback starts\n"
+#endif
+#ifdef CONFIG_AUDIO_COREAUDIO
+ "-audiodev coreaudio,id=id[,prop[=value][,...]]\n"
+ " in|out.buffer-count= number of buffers\n"
+#endif
+#ifdef CONFIG_AUDIO_DSOUND
+ "-audiodev dsound,id=id[,prop[=value][,...]]\n"
+ " latency= add extra latency to playback in microseconds\n"
+#endif
+#ifdef CONFIG_AUDIO_OSS
+ "-audiodev oss,id=id[,prop[=value][,...]]\n"
+ " in|out.dev= path of the audio device to use\n"
+ " in|out.buffer-count= number of buffers\n"
+ " in|out.try-poll= attempt to use poll mode\n"
+ " try-mmap= try using memory mapped access\n"
+ " exclusive= open device in exclusive mode\n"
+ " dsp-policy= set timing policy (0..10), -1 to use fragment mode\n"
+#endif
+#ifdef CONFIG_AUDIO_PA
+ "-audiodev pa,id=id[,prop[=value][,...]]\n"
+ " server= PulseAudio server address\n"
+ " in|out.name= source/sink device name\n"
+#endif
+#ifdef CONFIG_AUDIO_SDL
+ "-audiodev sdl,id=id[,prop[=value][,...]]\n"
+#endif
+#ifdef CONFIG_SPICE
+ "-audiodev spice,id=id[,prop[=value][,...]]\n"
+#endif
+ "-audiodev wav,id=id[,prop[=value][,...]]\n"
+ " path= path of wav file to record\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -audiodev [driver=]@var{driver},id=@var{id}[,@var{prop}[=@var{value}][,...]]
+@findex -audiodev
+Adds a new audio backend @var{driver} identified by @var{id}. There are
+global and driver specific properties. Some values can be set
+differently for input and output, they're marked with @code{in|out.}.
+You can set the input's property with @code{in.@var{prop}} and the
+output's property with @code{out.@var{prop}}. For example:
+@example
+-audiodev alsa,id=example,in.frequency=44110,out.frequency=8000
+-audiodev alsa,id=example,out.channels=1 # leaves in.channels unspecified
+@end example
+
+Valid global options are:
+
+@table @option
+@item id=@var{identifier}
+Identifies the audio backend.
+
+@item timer-period=@var{period}
+Sets the timer @var{period} used by the audio subsystem in microseconds.
+Default is 10000 (10 ms).
+
+@item in|out.fixed-settings=on|off
+Use fixed settings for host audio. When off, it will change based on
+how the guest opens the sound card. In this case you must not specify
+@var{frequency}, @var{channels} or @var{format}. Default is on.
+
+@item in|out.frequency=@var{frequency}
+Specify the @var{frequency} to use when using @var{fixed-settings}.
+Default is 44100Hz.
+
+@item in|out.channels=@var{channels}
+Specify the number of @var{channels} to use when using
+@var{fixed-settings}. Default is 2 (stereo).
+
+@item in|out.format=@var{format}
+Specify the sample @var{format} to use when using @var{fixed-settings}.
+Valid values are: @code{s8}, @code{s16}, @code{s32}, @code{u8},
+@code{u16}, @code{u32}. Default is @code{s16}.
+
+@item in|out.voices=@var{voices}
+Specify the number of @var{voices} to use. Default is 1.
+
+@item in|out.buffer=@var{usecs}
+Sets the size of the buffer in microseconds.
+
+@end table
+
+@item -audiodev none,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a dummy backend that discards all outputs. This backend has no
+backend specific properties.
+
+@item -audiodev alsa,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates backend using the ALSA. This backend is only available on
+Linux.
+
+ALSA specific options are:
+
+@table @option
+
+@item in|out.dev=@var{device}
+Specify the ALSA @var{device} to use for input and/or output. Default
+is @code{default}.
+
+@item in|out.period-len=@var{usecs}
+Sets the period length in microseconds.
+
+@item in|out.try-poll=on|off
+Attempt to use poll mode with the device. Default is on.
+
+@item threshold=@var{threshold}
+Threshold (in microseconds) when playback starts. Default is 0.
+
+@end table
+
+@item -audiodev coreaudio,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using Apple's Core Audio. This backend is only
+available on Mac OS and only supports playback.
+
+Core Audio specific options are:
+
+@table @option
+
+@item in|out.buffer-count=@var{count}
+Sets the @var{count} of the buffers.
+
+@end table
+
+@item -audiodev dsound,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using Microsoft's DirectSound. This backend is only
+available on Windows and only supports playback.
+
+DirectSound specific options are:
+
+@table @option
+
+@item latency=@var{usecs}
+Add extra @var{usecs} microseconds latency to playback. Default is
+10000 (10 ms).
+
+@end table
+
+@item -audiodev oss,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using OSS. This backend is available on most
+Unix-like systems.
+
+OSS specific options are:
+
+@table @option
+
+@item in|out.dev=@var{device}
+Specify the file name of the OSS @var{device} to use. Default is
+@code{/dev/dsp}.
+
+@item in|out.buffer-count=@var{count}
+Sets the @var{count} of the buffers.
+
+@item in|out.try-poll=on|of
+Attempt to use poll mode with the device. Default is on.
+
+@item try-mmap=on|off
+Try using memory mapped device access. Default is off.
+
+@item exclusive=on|off
+Open the device in exclusive mode (vmix won't work in this case).
+Default is off.
+
+@item dsp-policy=@var{policy}
+Sets the timing policy (between 0 and 10, where smaller number means
+smaller latency but higher CPU usage). Use -1 to use buffer sizes
+specified by @code{buffer} and @code{buffer-count}. This option is
+ignored if you do not have OSS 4. Default is 5.
+
+@end table
+
+@item -audiodev pa,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using PulseAudio. This backend is available on most
+systems.
+
+PulseAudio specific options are:
+
+@table @option
+
+@item server=@var{server}
+Sets the PulseAudio @var{server} to connect to.
+
+@item in|out.name=@var{sink}
+Use the specified source/sink for recording/playback.
+
+@end table
+
+@item -audiodev sdl,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using SDL. This backend is available on most systems,
+but you should use your platform's native backend if possible. This
+backend has no backend specific properties.
+
+@item -audiodev spice,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend that sends audio through SPICE. This backend requires
+@code{-spice} and automatically selected in that case, so usually you
+can ignore this option. This backend has no backend specific
+properties.
+
+@item -audiodev wav,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend that writes audio to a WAV file.
+
+Backend specific options are:
+
+@table @option
+
+@item path=@var{path}
+Write recorded audio into the specified file. Default is
+@code{qemu.wav}.
+
+@end table
ETEXI
DEF("soundhw", HAS_ARG, QEMU_OPTION_soundhw,
diff --git a/scripts/decodetree.py b/scripts/decodetree.py
index e342d278b8..aa790b596a 100755
--- a/scripts/decodetree.py
+++ b/scripts/decodetree.py
@@ -17,139 +17,7 @@
#
# Generate a decoding tree from a specification file.
-#
-# The tree is built from instruction "patterns". A pattern may represent
-# a single architectural instruction or a group of same, depending on what
-# is convenient for further processing.
-#
-# Each pattern has "fixedbits" & "fixedmask", the combination of which
-# describes the condition under which the pattern is matched:
-#
-# (insn & fixedmask) == fixedbits
-#
-# Each pattern may have "fields", which are extracted from the insn and
-# passed along to the translator. Examples of such are registers,
-# immediates, and sub-opcodes.
-#
-# In support of patterns, one may declare fields, argument sets, and
-# formats, each of which may be re-used to simplify further definitions.
-#
-# *** Field syntax:
-#
-# field_def := '%' identifier ( unnamed_field )+ ( !function=identifier )?
-# unnamed_field := number ':' ( 's' ) number
-#
-# For unnamed_field, the first number is the least-significant bit position of
-# the field and the second number is the length of the field. If the 's' is
-# present, the field is considered signed. If multiple unnamed_fields are
-# present, they are concatenated. In this way one can define disjoint fields.
-#
-# If !function is specified, the concatenated result is passed through the
-# named function, taking and returning an integral value.
-#
-# FIXME: the fields of the structure into which this result will be stored
-# is restricted to "int". Which means that we cannot expand 64-bit items.
-#
-# Field examples:
-#
-# %disp 0:s16 -- sextract(i, 0, 16)
-# %imm9 16:6 10:3 -- extract(i, 16, 6) << 3 | extract(i, 10, 3)
-# %disp12 0:s1 1:1 2:10 -- sextract(i, 0, 1) << 11
-# | extract(i, 1, 1) << 10
-# | extract(i, 2, 10)
-# %shimm8 5:s8 13:1 !function=expand_shimm8
-# -- expand_shimm8(sextract(i, 5, 8) << 1
-# | extract(i, 13, 1))
-#
-# *** Argument set syntax:
-#
-# args_def := '&' identifier ( args_elt )+ ( !extern )?
-# args_elt := identifier
-#
-# Each args_elt defines an argument within the argument set.
-# Each argument set will be rendered as a C structure "arg_$name"
-# with each of the fields being one of the member arguments.
-#
-# If !extern is specified, the backing structure is assumed to
-# have been already declared, typically via a second decoder.
-#
-# Argument set examples:
-#
-# &reg3 ra rb rc
-# &loadstore reg base offset
-#
-# *** Format syntax:
-#
-# fmt_def := '@' identifier ( fmt_elt )+
-# fmt_elt := fixedbit_elt | field_elt | field_ref | args_ref
-# fixedbit_elt := [01.-]+
-# field_elt := identifier ':' 's'? number
-# field_ref := '%' identifier | identifier '=' '%' identifier
-# args_ref := '&' identifier
-#
-# Defining a format is a handy way to avoid replicating groups of fields
-# across many instruction patterns.
-#
-# A fixedbit_elt describes a contiguous sequence of bits that must
-# be 1, 0, [.-] for don't care. The difference between '.' and '-'
-# is that '.' means that the bit will be covered with a field or a
-# final [01] from the pattern, and '-' means that the bit is really
-# ignored by the cpu and will not be specified.
-#
-# A field_elt describes a simple field only given a width; the position of
-# the field is implied by its position with respect to other fixedbit_elt
-# and field_elt.
-#
-# If any fixedbit_elt or field_elt appear then all bits must be defined.
-# Padding with a fixedbit_elt of all '.' is an easy way to accomplish that.
-#
-# A field_ref incorporates a field by reference. This is the only way to
-# add a complex field to a format. A field may be renamed in the process
-# via assignment to another identifier. This is intended to allow the
-# same argument set be used with disjoint named fields.
-#
-# A single args_ref may specify an argument set to use for the format.
-# The set of fields in the format must be a subset of the arguments in
-# the argument set. If an argument set is not specified, one will be
-# inferred from the set of fields.
-#
-# It is recommended, but not required, that all field_ref and args_ref
-# appear at the end of the line, not interleaving with fixedbit_elf or
-# field_elt.
-#
-# Format examples:
-#
-# @opr ...... ra:5 rb:5 ... 0 ....... rc:5
-# @opi ...... ra:5 lit:8 1 ....... rc:5
-#
-# *** Pattern syntax:
-#
-# pat_def := identifier ( pat_elt )+
-# pat_elt := fixedbit_elt | field_elt | field_ref
-# | args_ref | fmt_ref | const_elt
-# fmt_ref := '@' identifier
-# const_elt := identifier '=' number
-#
-# The fixedbit_elt and field_elt specifiers are unchanged from formats.
-# A pattern that does not specify a named format will have one inferred
-# from a referenced argument set (if present) and the set of fields.
-#
-# A const_elt allows a argument to be set to a constant value. This may
-# come in handy when fields overlap between patterns and one has to
-# include the values in the fixedbit_elt instead.
-#
-# The decoder will call a translator function for each pattern matched.
-#
-# Pattern examples:
-#
-# addl_r 010000 ..... ..... .... 0000000 ..... @opr
-# addl_i 010000 ..... ..... .... 0000000 ..... @opi
-#
-# which will, in part, invoke
-#
-# trans_addl_r(ctx, &arg_opr, insn)
-# and
-# trans_addl_i(ctx, &arg_opi, insn)
+# See the syntax and semantics in docs/devel/decodetree.rst.
#
import os
@@ -163,6 +31,7 @@ fields = {}
arguments = {}
formats = {}
patterns = []
+allpatterns = []
translate_prefix = 'trans'
translate_scope = 'static '
@@ -432,13 +301,7 @@ class General:
self.fields = flds
def __str__(self):
- r = self.name
- if self.base:
- r = r + ' ' + self.base.name
- else:
- r = r + ' ' + str(self.fields)
- r = r + ' ' + str_match_bits(self.fixedbits, self.fixedmask)
- return r
+ return self.name + ' ' + str_match_bits(self.fixedbits, self.fixedmask)
def str1(self, i):
return str_indent(i) + self.__str__()
@@ -449,7 +312,8 @@ class Format(General):
"""Class representing an instruction format"""
def extract_name(self):
- return 'extract_' + self.name
+ global decode_function
+ return decode_function + '_extract_' + self.name
def output_extract(self):
output('static void ', self.extract_name(), '(',
@@ -480,11 +344,52 @@ class Pattern(General):
output(ind, self.base.extract_name(), '(&u.f_', arg, ', insn);\n')
for n, f in self.fields.items():
output(ind, 'u.f_', arg, '.', n, ' = ', f.str_extract(), ';\n')
- output(ind, 'return ', translate_prefix, '_', self.name,
- '(ctx, &u.f_', arg, ');\n')
+ output(ind, 'if (', translate_prefix, '_', self.name,
+ '(ctx, &u.f_', arg, ')) return true;\n')
# end Pattern
+class MultiPattern(General):
+ """Class representing an overlapping set of instruction patterns"""
+
+ def __init__(self, lineno, pats, fixb, fixm, udfm):
+ self.file = input_file
+ self.lineno = lineno
+ self.pats = pats
+ self.base = None
+ self.fixedbits = fixb
+ self.fixedmask = fixm
+ self.undefmask = udfm
+
+ def __str__(self):
+ r = "{"
+ for p in self.pats:
+ r = r + ' ' + str(p)
+ return r + "}"
+
+ def output_decl(self):
+ for p in self.pats:
+ p.output_decl()
+
+ def output_code(self, i, extracted, outerbits, outermask):
+ global translate_prefix
+ ind = str_indent(i)
+ for p in self.pats:
+ if outermask != p.fixedmask:
+ innermask = p.fixedmask & ~outermask
+ innerbits = p.fixedbits & ~outermask
+ output(ind, 'if ((insn & ',
+ '0x{0:08x}) == 0x{1:08x}'.format(innermask, innerbits),
+ ') {\n')
+ output(ind, ' /* ',
+ str_match_bits(p.fixedbits, p.fixedmask), ' */\n')
+ p.output_code(i + 4, extracted, p.fixedbits, p.fixedmask)
+ output(ind, '}\n')
+ else:
+ p.output_code(i, extracted, p.fixedbits, p.fixedmask)
+#end MultiPattern
+
+
def parse_field(lineno, name, toks):
"""Parse one instruction field from TOKS at LINENO"""
global fields
@@ -637,6 +542,7 @@ def parse_generic(lineno, is_format, name, toks):
global arguments
global formats
global patterns
+ global allpatterns
global re_ident
global insnwidth
global insnmask
@@ -684,7 +590,7 @@ def parse_generic(lineno, is_format, name, toks):
continue
# 'Foo=number' sets an argument field to a constant value
- if re_fullmatch(re_ident + '=[0-9]+', t):
+ if re_fullmatch(re_ident + '=[+-]?[0-9]+', t):
(fname, value) = t.split('=')
value = int(value)
flds = add_field(lineno, flds, fname, ConstField(value))
@@ -716,6 +622,8 @@ def parse_generic(lineno, is_format, name, toks):
sign = True
flen = flen[1:]
shift = int(flen, 10)
+ if shift + width > insnwidth:
+ error(lineno, 'field {0} exceeds insnwidth'.format(fname))
f = Field(sign, insnwidth - width - shift, shift)
flds = add_field(lineno, flds, fname, f)
fixedbits <<= shift
@@ -781,6 +689,7 @@ def parse_generic(lineno, is_format, name, toks):
pat = Pattern(name, lineno, fmt, fixedbits, fixedmask,
undefmask, fieldmask, flds)
patterns.append(pat)
+ allpatterns.append(pat)
# Validate the masks that we have assembled.
if fieldmask & fixedmask:
@@ -799,17 +708,66 @@ def parse_generic(lineno, is_format, name, toks):
.format(allbits ^ insnmask))
# end parse_general
+def build_multi_pattern(lineno, pats):
+ """Validate the Patterns going into a MultiPattern."""
+ global patterns
+ global insnmask
+
+ if len(pats) < 2:
+ error(lineno, 'less than two patterns within braces')
+
+ fixedmask = insnmask
+ undefmask = insnmask
+
+ # Collect fixed/undefmask for all of the children.
+ # Move the defining lineno back to that of the first child.
+ for p in pats:
+ fixedmask &= p.fixedmask
+ undefmask &= p.undefmask
+ if p.lineno < lineno:
+ lineno = p.lineno
+
+ repeat = True
+ while repeat:
+ if fixedmask == 0:
+ error(lineno, 'no overlap in patterns within braces')
+ fixedbits = None
+ for p in pats:
+ thisbits = p.fixedbits & fixedmask
+ if fixedbits is None:
+ fixedbits = thisbits
+ elif fixedbits != thisbits:
+ fixedmask &= ~(fixedbits ^ thisbits)
+ break
+ else:
+ repeat = False
+
+ mp = MultiPattern(lineno, pats, fixedbits, fixedmask, undefmask)
+ patterns.append(mp)
+# end build_multi_pattern
def parse_file(f):
"""Parse all of the patterns within a file"""
+ global patterns
+
# Read all of the lines of the file. Concatenate lines
# ending in backslash; discard empty lines and comments.
toks = []
lineno = 0
+ nesting = 0
+ saved_pats = []
+
for line in f:
lineno += 1
+ # Expand and strip spaces, to find indent.
+ line = line.rstrip()
+ line = line.expandtabs()
+ len1 = len(line)
+ line = line.lstrip()
+ len2 = len(line)
+
# Discard comments
end = line.find('#')
if end >= 0:
@@ -819,10 +777,18 @@ def parse_file(f):
if len(toks) != 0:
# Next line after continuation
toks.extend(t)
- elif len(t) == 0:
- # Empty line
- continue
else:
+ # Allow completely blank lines.
+ if len1 == 0:
+ continue
+ indent = len1 - len2
+ # Empty line due to comment.
+ if len(t) == 0:
+ # Indentation must be correct, even for comment lines.
+ if indent != nesting:
+ error(lineno, 'indentation ', indent, ' != ', nesting)
+ continue
+ start_lineno = lineno
toks = t
# Continuation?
@@ -830,21 +796,47 @@ def parse_file(f):
toks.pop()
continue
- if len(toks) < 2:
- error(lineno, 'short line')
-
name = toks[0]
del toks[0]
+ # End nesting?
+ if name == '}':
+ if nesting == 0:
+ error(start_lineno, 'mismatched close brace')
+ if len(toks) != 0:
+ error(start_lineno, 'extra tokens after close brace')
+ nesting -= 2
+ if indent != nesting:
+ error(start_lineno, 'indentation ', indent, ' != ', nesting)
+ pats = patterns
+ patterns = saved_pats.pop()
+ build_multi_pattern(lineno, pats)
+ toks = []
+ continue
+
+ # Everything else should have current indentation.
+ if indent != nesting:
+ error(start_lineno, 'indentation ', indent, ' != ', nesting)
+
+ # Start nesting?
+ if name == '{':
+ if len(toks) != 0:
+ error(start_lineno, 'extra tokens after open brace')
+ saved_pats.append(patterns)
+ patterns = []
+ nesting += 2
+ toks = []
+ continue
+
# Determine the type of object needing to be parsed.
if name[0] == '%':
- parse_field(lineno, name[1:], toks)
+ parse_field(start_lineno, name[1:], toks)
elif name[0] == '&':
- parse_arguments(lineno, name[1:], toks)
+ parse_arguments(start_lineno, name[1:], toks)
elif name[0] == '@':
- parse_generic(lineno, True, name[1:], toks)
+ parse_generic(start_lineno, True, name[1:], toks)
else:
- parse_generic(lineno, False, name, toks)
+ parse_generic(start_lineno, False, name, toks)
toks = []
# end parse_file
@@ -909,23 +901,22 @@ class Tree:
output(ind, ' /* ',
str_match_bits(innerbits, innermask), ' */\n')
s.output_code(i + 4, extracted, innerbits, innermask)
+ output(ind, ' return false;\n')
output(ind, '}\n')
- output(ind, 'return false;\n')
# end Tree
def build_tree(pats, outerbits, outermask):
# Find the intersection of all remaining fixedmask.
- innermask = ~outermask
+ innermask = ~outermask & insnmask
for i in pats:
innermask &= i.fixedmask
if innermask == 0:
- pnames = []
+ text = 'overlapping patterns:'
for p in pats:
- pnames.append(p.name + ':' + p.file + ':' + str(p.lineno))
- error_with_file(pats[0].file, pats[0].lineno,
- 'overlapping patterns:', pnames)
+ text += '\n' + p.file + ':' + str(p.lineno) + ': ' + str(p)
+ error_with_file(pats[0].file, pats[0].lineno, text)
fullmask = outermask | innermask
@@ -978,6 +969,7 @@ def main():
global arguments
global formats
global patterns
+ global allpatterns
global translate_scope
global translate_prefix
global output_fd
@@ -990,7 +982,8 @@ def main():
decode_scope = 'static '
- long_opts = ['decode=', 'translate=', 'output=', 'insnwidth=']
+ long_opts = ['decode=', 'translate=', 'output=', 'insnwidth=',
+ 'static-decode=']
try:
(opts, args) = getopt.getopt(sys.argv[1:], 'o:w:', long_opts)
except getopt.GetoptError as err:
@@ -1001,6 +994,8 @@ def main():
elif o == '--decode':
decode_function = a
decode_scope = ''
+ elif o == '--static-decode':
+ decode_function = a
elif o == '--translate':
translate_prefix = a
translate_scope = ''
@@ -1039,7 +1034,7 @@ def main():
# Make sure that the argument sets are the same, and declare the
# function only once.
out_pats = {}
- for i in patterns:
+ for i in allpatterns:
if i.name in out_pats:
p = out_pats[i.name]
if i.base.base != p.base.base:
@@ -1057,14 +1052,16 @@ def main():
'(DisasContext *ctx, ', insntype, ' insn)\n{\n')
i4 = str_indent(4)
- output(i4, 'union {\n')
- for n in sorted(arguments.keys()):
- f = arguments[n]
- output(i4, i4, f.struct_name(), ' f_', f.name, ';\n')
- output(i4, '} u;\n\n')
- t.output_code(4, False, 0, 0)
+ if len(allpatterns) != 0:
+ output(i4, 'union {\n')
+ for n in sorted(arguments.keys()):
+ f = arguments[n]
+ output(i4, i4, f.struct_name(), ' f_', f.name, ';\n')
+ output(i4, '} u;\n\n')
+ t.output_code(4, False, 0, 0)
+ output(i4, 'return false;\n')
output('}\n')
if output_file:
diff --git a/scripts/qemugdb/timers.py b/scripts/qemugdb/timers.py
index 51ea04b5e2..f0e132d27a 100644
--- a/scripts/qemugdb/timers.py
+++ b/scripts/qemugdb/timers.py
@@ -6,8 +6,10 @@
#
# Author: Alex Bennée <alex.bennee@linaro.org>
#
-# This work is licensed under the terms of the GNU GPL, version 2. See
-# the COPYING file in the top-level directory.
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
# 'qemu timers' -- display the current timerlists
diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 9fec46e2ed..7776c7b141 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -73,7 +73,7 @@ import sys
import os
import errno
import atexit
-import shlex
+import re
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu import qmp
@@ -222,7 +222,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
< command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
"""
- cmdargs = shlex.split(cmdline)
+ cmdargs = re.findall(r'''(?:[^\s"']|"(?:\\.|[^"])*"|'(?:\\.|[^'])*')+''', cmdline)
# Transactional CLI entry/exit:
if cmdargs[0] == 'transaction(':
diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h
index 861bbb1f16..c062c7969c 100644
--- a/target/hppa/cpu.h
+++ b/target/hppa/cpu.h
@@ -143,6 +143,10 @@
#endif
#define CR_RC 0
+#define CR_PID1 8
+#define CR_PID2 9
+#define CR_PID3 12
+#define CR_PID4 13
#define CR_SCRCCR 10
#define CR_SAR 11
#define CR_IVA 14
@@ -341,6 +345,12 @@ target_ureg cpu_hppa_get_psw(CPUHPPAState *env);
void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg);
void cpu_hppa_loaded_fr0(CPUHPPAState *env);
+#ifdef CONFIG_USER_ONLY
+static inline void cpu_hppa_change_prot_id(CPUHPPAState *env) { }
+#else
+void cpu_hppa_change_prot_id(CPUHPPAState *env);
+#endif
+
#define cpu_signal_handler cpu_hppa_signal_handler
int cpu_hppa_signal_handler(int host_signum, void *pinfo, void *puc);
diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c
index 3157a690f2..983bf92aaf 100644
--- a/target/hppa/gdbstub.c
+++ b/target/hppa/gdbstub.c
@@ -93,19 +93,19 @@ int hppa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
val = env->cr[CR_RC];
break;
case 52:
- val = env->cr[8];
+ val = env->cr[CR_PID1];
break;
case 53:
- val = env->cr[9];
+ val = env->cr[CR_PID2];
break;
case 54:
val = env->cr[CR_SCRCCR];
break;
case 55:
- val = env->cr[12];
+ val = env->cr[CR_PID3];
break;
case 56:
- val = env->cr[13];
+ val = env->cr[CR_PID4];
break;
case 57:
val = env->cr[24];
@@ -224,19 +224,23 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
env->cr[CR_RC] = val;
break;
case 52:
- env->cr[8] = val;
+ env->cr[CR_PID1] = val;
+ cpu_hppa_change_prot_id(env);
break;
case 53:
- env->cr[9] = val;
+ env->cr[CR_PID2] = val;
+ cpu_hppa_change_prot_id(env);
break;
case 54:
env->cr[CR_SCRCCR] = val;
break;
case 55:
- env->cr[12] = val;
+ env->cr[CR_PID3] = val;
+ cpu_hppa_change_prot_id(env);
break;
case 56:
- env->cr[13] = val;
+ env->cr[CR_PID4] = val;
+ cpu_hppa_change_prot_id(env);
break;
case 57:
env->cr[24] = val;
diff --git a/target/hppa/helper.c b/target/hppa/helper.c
index 6539061e52..ac750b62ef 100644
--- a/target/hppa/helper.c
+++ b/target/hppa/helper.c
@@ -21,6 +21,7 @@
#include "cpu.h"
#include "fpu/softfloat.h"
+#include "exec/exec-all.h"
#include "exec/helper-proto.h"
target_ureg cpu_hppa_get_psw(CPUHPPAState *env)
@@ -49,6 +50,7 @@ target_ureg cpu_hppa_get_psw(CPUHPPAState *env)
void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw)
{
+ target_ureg old_psw = env->psw;
target_ureg cb = 0;
env->psw = psw & ~(PSW_N | PSW_V | PSW_CB);
@@ -64,6 +66,14 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw)
cb |= ((psw >> 9) & 1) << 8;
cb |= ((psw >> 8) & 1) << 4;
env->psw_cb = cb;
+
+ /* If PSW_P changes, it affects how we translate addresses. */
+ if ((psw ^ old_psw) & PSW_P) {
+#ifndef CONFIG_USER_ONLY
+ CPUState *src = CPU(hppa_env_get_cpu(env));
+ tlb_flush_by_mmuidx(src, 0xf);
+#endif
+ }
}
void hppa_cpu_dump_state(CPUState *cs, FILE *f,
diff --git a/target/hppa/helper.h b/target/hppa/helper.h
index bfe0dd1db1..38d834ef6b 100644
--- a/target/hppa/helper.h
+++ b/target/hppa/helper.h
@@ -92,4 +92,5 @@ DEF_HELPER_FLAGS_3(itlbp, TCG_CALL_NO_RWG, void, env, tl, tr)
DEF_HELPER_FLAGS_2(ptlb, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_FLAGS_1(ptlbe, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_FLAGS_2(lpa, TCG_CALL_NO_WG, tr, env, tl)
+DEF_HELPER_FLAGS_1(change_prot_id, TCG_CALL_NO_RWG, void, env)
#endif
diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode
index 55ff39dd05..098370c2f0 100644
--- a/target/hppa/insns.decode
+++ b/target/hppa/insns.decode
@@ -525,3 +525,6 @@ fmpy_d 001110 ..... ..... 010 ..... ... ..... @f0e_d_3
fdiv_d 001110 ..... ..... 011 ..... ... ..... @f0e_d_3
xmpyu 001110 ..... ..... 010 .0111 .00 t:5 r1=%ra64 r2=%rb64
+
+# diag
+diag 000101 ----- ----- ---- ---- ---- ----
diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c
index aecf3075f6..c9b57d07c3 100644
--- a/target/hppa/mem_helper.c
+++ b/target/hppa/mem_helper.c
@@ -22,6 +22,7 @@
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#include "qom/cpu.h"
+#include "trace.h"
#ifdef CONFIG_USER_ONLY
int hppa_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
@@ -43,9 +44,12 @@ static hppa_tlb_entry *hppa_find_tlb(CPUHPPAState *env, vaddr addr)
for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) {
hppa_tlb_entry *ent = &env->tlb[i];
if (ent->va_b <= addr && addr <= ent->va_e) {
+ trace_hppa_tlb_find_entry(env, ent + i, ent->entry_valid,
+ ent->va_b, ent->va_e, ent->pa);
return ent;
}
}
+ trace_hppa_tlb_find_entry_not_found(env, addr);
return NULL;
}
@@ -55,6 +59,8 @@ static void hppa_flush_tlb_ent(CPUHPPAState *env, hppa_tlb_entry *ent)
unsigned i, n = 1 << (2 * ent->page_size);
uint64_t addr = ent->va_b;
+ trace_hppa_tlb_flush_ent(env, ent, ent->va_b, ent->va_e, ent->pa);
+
for (i = 0; i < n; ++i, addr += TARGET_PAGE_SIZE) {
/* Do not flush MMU_PHYS_IDX. */
tlb_flush_page_by_mmuidx(cs, addr, 0xf);
@@ -96,9 +102,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
if (ent == NULL || !ent->entry_valid) {
phys = 0;
prot = 0;
- /* ??? Unconditionally report data tlb miss,
- even if this is an instruction fetch. */
- ret = EXCP_DTLB_MISS;
+ ret = (type == PAGE_EXEC) ? EXCP_ITLB_MISS : EXCP_DTLB_MISS;
goto egress;
}
@@ -127,7 +131,20 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
break;
}
- /* ??? Check PSW_P and ent->access_prot. This can remove PAGE_WRITE. */
+ /* access_id == 0 means public page and no check is performed */
+ if ((env->psw & PSW_P) && ent->access_id) {
+ /* If bits [31:1] match, and bit 0 is set, suppress write. */
+ int match = ent->access_id * 2 + 1;
+
+ if (match == env->cr[CR_PID1] || match == env->cr[CR_PID2] ||
+ match == env->cr[CR_PID3] || match == env->cr[CR_PID4]) {
+ prot &= PAGE_READ | PAGE_EXEC;
+ if (type == PAGE_WRITE) {
+ ret = EXCP_DMPI;
+ goto egress;
+ }
+ }
+ }
/* No guest access type indicates a non-architectural access from
within QEMU. Bypass checks for access, D, B and T bits. */
@@ -171,6 +188,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
egress:
*pphys = phys;
*pprot = prot;
+ trace_hppa_tlb_get_physical_address(env, ret, prot, addr, phys);
return ret;
}
@@ -200,6 +218,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size,
MMUAccessType type, int mmu_idx, uintptr_t retaddr)
{
HPPACPU *cpu = HPPA_CPU(cs);
+ CPUHPPAState *env = &cpu->env;
int prot, excp, a_prot;
hwaddr phys;
@@ -215,9 +234,10 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size,
break;
}
- excp = hppa_get_physical_address(&cpu->env, addr, mmu_idx,
+ excp = hppa_get_physical_address(env, addr, mmu_idx,
a_prot, &phys, &prot);
if (unlikely(excp >= 0)) {
+ trace_hppa_tlb_fill_excp(env, addr, size, type, mmu_idx);
/* Failure. Raise the indicated exception. */
cs->exception_index = excp;
if (cpu->env.psw & PSW_Q) {
@@ -228,6 +248,8 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size,
cpu_loop_exit_restore(cs, retaddr);
}
+ trace_hppa_tlb_fill_success(env, addr & TARGET_PAGE_MASK,
+ phys & TARGET_PAGE_MASK, size, type, mmu_idx);
/* Success! Store the translation into the QEMU TLB. */
tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
prot, mmu_idx, TARGET_PAGE_SIZE);
@@ -242,11 +264,13 @@ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
/* Zap any old entries covering ADDR; notice empty entries on the way. */
for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) {
hppa_tlb_entry *ent = &env->tlb[i];
- if (!ent->entry_valid) {
- empty = ent;
- } else if (ent->va_b <= addr && addr <= ent->va_e) {
- hppa_flush_tlb_ent(env, ent);
- empty = ent;
+ if (ent->va_b <= addr && addr <= ent->va_e) {
+ if (ent->entry_valid) {
+ hppa_flush_tlb_ent(env, ent);
+ }
+ if (!empty) {
+ empty = ent;
+ }
}
}
@@ -259,6 +283,7 @@ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
empty->va_b = addr & TARGET_PAGE_MASK;
empty->va_e = empty->va_b + TARGET_PAGE_SIZE - 1;
empty->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS;
+ trace_hppa_tlb_itlba(env, empty, empty->va_b, empty->va_e, empty->pa);
}
/* Insert (Insn/Data) TLB Protection. Note this is PA 1.1 only. */
@@ -266,7 +291,7 @@ void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
{
hppa_tlb_entry *ent = hppa_find_tlb(env, addr);
- if (unlikely(ent == NULL || ent->entry_valid)) {
+ if (unlikely(ent == NULL)) {
qemu_log_mask(LOG_GUEST_ERROR, "ITLBP not following ITLBA\n");
return;
}
@@ -280,6 +305,8 @@ void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
ent->d = extract32(reg, 28, 1);
ent->t = extract32(reg, 29, 1);
ent->entry_valid = 1;
+ trace_hppa_tlb_itlbp(env, ent, ent->access_id, ent->u, ent->ar_pl2,
+ ent->ar_pl1, ent->ar_type, ent->b, ent->d, ent->t);
}
/* Purge (Insn/Data) TLB. This is explicitly page-based, and is
@@ -299,6 +326,7 @@ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr)
{
CPUState *src = CPU(hppa_env_get_cpu(env));
CPUState *cpu;
+ trace_hppa_tlb_ptlb(env);
run_on_cpu_data data = RUN_ON_CPU_TARGET_PTR(addr);
CPU_FOREACH(cpu) {
@@ -314,11 +342,24 @@ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr)
void HELPER(ptlbe)(CPUHPPAState *env)
{
CPUState *src = CPU(hppa_env_get_cpu(env));
-
+ trace_hppa_tlb_ptlbe(env);
memset(env->tlb, 0, sizeof(env->tlb));
tlb_flush_by_mmuidx(src, 0xf);
}
+void cpu_hppa_change_prot_id(CPUHPPAState *env)
+{
+ if (env->psw & PSW_P) {
+ CPUState *src = CPU(hppa_env_get_cpu(env));
+ tlb_flush_by_mmuidx(src, 0xf);
+ }
+}
+
+void HELPER(change_prot_id)(CPUHPPAState *env)
+{
+ cpu_hppa_change_prot_id(env);
+}
+
target_ureg HELPER(lpa)(CPUHPPAState *env, target_ulong addr)
{
hwaddr phys;
@@ -335,8 +376,10 @@ target_ureg HELPER(lpa)(CPUHPPAState *env, target_ulong addr)
if (excp == EXCP_DTLB_MISS) {
excp = EXCP_NA_DTLB_MISS;
}
+ trace_hppa_tlb_lpa_failed(env, addr);
hppa_dynamic_excp(env, excp, GETPC());
}
+ trace_hppa_tlb_lpa_success(env, addr, phys);
return phys;
}
diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c
index 268caaaa20..a55a5dfc02 100644
--- a/target/hppa/op_helper.c
+++ b/target/hppa/op_helper.c
@@ -25,6 +25,7 @@
#include "sysemu/sysemu.h"
#include "qemu/timer.h"
#include "fpu/softfloat.h"
+#include "trace.h"
void QEMU_NORETURN HELPER(excp)(CPUHPPAState *env, int excp)
{
@@ -165,6 +166,7 @@ target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr,
int prot, excp;
hwaddr phys;
+ trace_hppa_tlb_probe(addr, level, want);
/* Fail if the requested privilege level is higher than current. */
if (level < (env->iaoq_f & 3)) {
return 0;
@@ -676,11 +678,6 @@ target_ureg HELPER(swap_system_mask)(CPUHPPAState *env, target_ureg nsm)
void HELPER(rfi)(CPUHPPAState *env)
{
- /* ??? On second reading this condition simply seems
- to be undefined rather than a diagnosed trap. */
- if (env->psw & (PSW_I | PSW_R | PSW_Q)) {
- helper_excp(env, EXCP_ILL);
- }
env->iasq_f = (uint64_t)env->cr[CR_IIASQ] << 32;
env->iasq_b = (uint64_t)env->cr_back[0] << 32;
env->iaoq_f = env->cr[CR_IIAOQ];
diff --git a/target/hppa/trace-events b/target/hppa/trace-events
new file mode 100644
index 0000000000..80dae5bd8b
--- /dev/null
+++ b/target/hppa/trace-events
@@ -0,0 +1,18 @@
+# See docs/devel/tracing.txt for syntax documentation.
+
+# target/hppa/mem_helper.c
+disable hppa_tlb_flush_ent(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%lx"
+disable hppa_tlb_find_entry(void *env, void *ent, int valid, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p valid=%d va_b=0x%lx va_e=0x%lx pa=0x%lx"
+disable hppa_tlb_find_entry_not_found(void *env, uint64_t addr) "env=%p addr=%08lx"
+disable hppa_tlb_get_physical_address(void *env, int ret, int prot, uint64_t addr, uint64_t phys) "env=%p ret=%d prot=%d addr=0x%lx phys=0x%lx"
+disable hppa_tlb_fill_excp(void *env, uint64_t addr, int size, int type, int mmu_idx) "env=%p addr=0x%lx size=%d type=%d mmu_idx=%d"
+disable hppa_tlb_fill_success(void *env, uint64_t addr, uint64_t phys, int size, int type, int mmu_idx) "env=%p addr=0x%lx phys=0x%lx size=%d type=%d mmu_idx=%d"
+disable hppa_tlb_itlba(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%lx"
+disable hppa_tlb_itlbp(void *env, void *ent, int access_id, int u, int pl2, int pl1, int type, int b, int d, int t) "env=%p ent=%p access_id=%x u=%d pl2=%d pl1=%d type=%d b=%d d=%d t=%d"
+disable hppa_tlb_ptlb(void *env) "env=%p"
+disable hppa_tlb_ptlbe(void *env) "env=%p"
+disable hppa_tlb_lpa_success(void *env, uint64_t addr, uint64_t phys) "env=%p addr=0x%lx phys=0x%lx"
+disable hppa_tlb_lpa_failed(void *env, uint64_t addr) "env=%p addr=0x%lx"
+
+# target/hppa/op_helper.c
+disable hppa_tlb_probe(uint64_t addr, int level, int want) "addr=0x%lx level=%d want=%d"
diff --git a/target/hppa/translate.c b/target/hppa/translate.c
index dc5636fe94..35c504087f 100644
--- a/target/hppa/translate.c
+++ b/target/hppa/translate.c
@@ -816,12 +816,10 @@ static bool gen_illegal(DisasContext *ctx)
static bool use_goto_tb(DisasContext *ctx, target_ureg dest)
{
- /* Suppress goto_tb in the case of single-steping and IO. */
- if ((tb_cflags(ctx->base.tb) & CF_LAST_IO)
- || ctx->base.singlestep_enabled) {
- return false;
- }
- return true;
+ /* Suppress goto_tb for page crossing, IO, or single-steping. */
+ return !(((ctx->base.pc_first ^ dest) & TARGET_PAGE_MASK)
+ || (tb_cflags(ctx->base.tb) & CF_LAST_IO)
+ || ctx->base.singlestep_enabled);
}
/* If the next insn is to be nullified, and it's on the same page,
@@ -2258,6 +2256,16 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a)
offsetof(CPUHPPAState, cr_back[ctl - CR_IIASQ]));
break;
+ case CR_PID1:
+ case CR_PID2:
+ case CR_PID3:
+ case CR_PID4:
+ tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl]));
+#ifndef CONFIG_USER_ONLY
+ gen_helper_change_prot_id(cpu_env);
+#endif
+ break;
+
default:
tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl]));
break;
@@ -2474,9 +2482,8 @@ static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a)
gen_helper_itlbp(cpu_env, addr, reg);
}
- /* Exit TB for ITLB change if mmu is enabled. This *should* not be
- the case, since the OS TLB fill handler runs with mmu disabled. */
- if (!a->data && (ctx->tb_flags & PSW_C)) {
+ /* Exit TB for TLB change if mmu is enabled. */
+ if (ctx->tb_flags & PSW_C) {
ctx->base.is_jmp = DISAS_IAQ_N_STALE;
}
return nullify_end(ctx);
@@ -2503,7 +2510,7 @@ static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a)
}
/* Exit TB for TLB change if mmu is enabled. */
- if (!a->data && (ctx->tb_flags & PSW_C)) {
+ if (ctx->tb_flags & PSW_C) {
ctx->base.is_jmp = DISAS_IAQ_N_STALE;
}
return nullify_end(ctx);
@@ -3033,7 +3040,7 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1,
DisasCond cond;
in2 = load_gpr(ctx, r);
- dest = dest_gpr(ctx, r);
+ dest = tcg_temp_new();
sv = NULL;
cb_msb = NULL;
@@ -3049,6 +3056,8 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1,
}
cond = do_cond(c * 2 + f, dest, cb_msb, sv);
+ save_gpr(ctx, r, dest);
+ tcg_temp_free(dest);
return do_cbranch(ctx, disp, n, &cond);
}
@@ -3446,6 +3455,8 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
{
target_ureg dest = iaoq_dest(ctx, a->disp);
+ nullify_over(ctx);
+
/* Make sure the caller hasn't done something weird with the queue.
* ??? This is not quite the same as the PSW[B] bit, which would be
* expensive to track. Real hardware will trap for
@@ -3483,7 +3494,16 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
}
#endif
- return do_dbranch(ctx, dest, a->l, a->n);
+ if (a->l) {
+ TCGv_reg tmp = dest_gpr(ctx, a->l);
+ if (ctx->privilege < 3) {
+ tcg_gen_andi_reg(tmp, tmp, -4);
+ }
+ tcg_gen_ori_reg(tmp, tmp, ctx->privilege);
+ save_gpr(ctx, a->l, tmp);
+ }
+
+ return do_dbranch(ctx, dest, 0, a->n);
}
static bool trans_blr(DisasContext *ctx, arg_blr *a)
@@ -4048,6 +4068,13 @@ static bool trans_fmpyfadd_d(DisasContext *ctx, arg_fmpyfadd_d *a)
return nullify_end(ctx);
}
+static bool trans_diag(DisasContext *ctx, arg_diag *a)
+{
+ qemu_log_mask(LOG_UNIMP, "DIAG opcode ignored\n");
+ cond_free(&ctx->null_cond);
+ return true;
+}
+
static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *ctx = container_of(dcbase, DisasContext, base);
diff --git a/tests/ahci-test.c b/tests/ahci-test.c
index 9f07e6f2ce..086811e602 100644
--- a/tests/ahci-test.c
+++ b/tests/ahci-test.c
@@ -38,7 +38,7 @@
#include "hw/pci/pci_regs.h"
/* TODO actually test the results and get rid of this */
-#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__))
+#define qmp_discard_response(s, ...) qobject_unref(qtest_qmp(s, __VA_ARGS__))
/* Test images sizes in MB */
#define TEST_IMAGE_SIZE_MB_LARGE (200 * 1024)
@@ -161,7 +161,6 @@ static AHCIQState *ahci_vboot(const char *cli, va_list ap)
s = g_new0(AHCIQState, 1);
s->parent = qtest_pc_vboot(cli, ap);
- global_qtest = s->parent->qts;
alloc_set_flags(&s->parent->alloc, ALLOC_LEAK_ASSERT);
/* Verify that we have an AHCI device present. */
@@ -201,7 +200,7 @@ static void ahci_shutdown(AHCIQState *ahci)
{
QOSState *qs = ahci->parent;
- set_context(qs);
+ assert(!global_qtest);
ahci_clean_mem(ahci);
free_ahci_device(ahci->dev);
g_free(ahci);
@@ -874,15 +873,15 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize,
/* Write some indicative pattern to our buffer. */
generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE);
- bufwrite(ptr, tx, bufsize);
+ qtest_bufwrite(ahci->parent->qts, ptr, tx, bufsize);
/* Write this buffer to disk, then read it back to the DMA buffer. */
ahci_guest_io(ahci, port, write_cmd, ptr, bufsize, sector);
- qmemset(ptr, 0x00, bufsize);
+ qtest_memset(ahci->parent->qts, ptr, 0x00, bufsize);
ahci_guest_io(ahci, port, read_cmd, ptr, bufsize, sector);
/*** Read back the Data ***/
- bufread(ptr, rx, bufsize);
+ qtest_bufread(ahci->parent->qts, ptr, rx, bufsize);
g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0);
ahci_free(ahci, ptr);
@@ -923,7 +922,7 @@ static void ahci_test_max(AHCIQState *ahci)
}
port = ahci_test_nondata(ahci, cmd);
- memread(ahci->port[port].fb + 0x40, d2h, 0x20);
+ qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x40, d2h, 0x20);
nsect = (uint64_t)d2h->lba_hi[2] << 40 |
(uint64_t)d2h->lba_hi[1] << 32 |
(uint64_t)d2h->lba_hi[0] << 24 |
@@ -1041,7 +1040,7 @@ static void test_dma_fragmented(void)
/* Create a DMA buffer in guest memory, and write our pattern to it. */
ptr = guest_alloc(&ahci->parent->alloc, bufsize);
g_assert(ptr);
- bufwrite(ptr, tx, bufsize);
+ qtest_bufwrite(ahci->parent->qts, ptr, tx, bufsize);
cmd = ahci_command_create(CMD_WRITE_DMA);
ahci_command_adjust(cmd, 0, ptr, bufsize, 32);
@@ -1058,7 +1057,7 @@ static void test_dma_fragmented(void)
ahci_command_free(cmd);
/* Read back the guest's receive buffer into local memory */
- bufread(ptr, rx, bufsize);
+ qtest_bufread(ahci->parent->qts, ptr, rx, bufsize);
guest_free(&ahci->parent->alloc, ptr);
g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0);
@@ -1169,8 +1168,6 @@ static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write)
"-drive if=ide,format=%s,file=%s "
"-incoming %s", imgfmt, tmp_path, uri);
- set_context(src->parent);
-
/* initialize */
px = ahci_port_select(src);
ahci_port_clear(src, px);
@@ -1238,7 +1235,7 @@ static void ahci_halted_io_test(uint8_t cmd_read, uint8_t cmd_write)
generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE);
ptr = ahci_alloc(ahci, bufsize);
g_assert(ptr);
- memwrite(ptr, tx, bufsize);
+ qtest_memwrite(ahci->parent->qts, ptr, tx, bufsize);
/* Attempt to write (and fail) */
cmd = ahci_guest_io_halt(ahci, port, cmd_write,
@@ -1304,8 +1301,6 @@ static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write)
"-incoming %s",
tmp_path, imgfmt, uri);
- set_context(src->parent);
-
/* Initialize and prepare */
port = ahci_port_select(src);
ahci_port_clear(src, port);
@@ -1314,7 +1309,7 @@ static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write)
/* create DMA source buffer and write pattern */
ptr = ahci_alloc(src, bufsize);
g_assert(ptr);
- memwrite(ptr, tx, bufsize);
+ qtest_memwrite(src->parent->qts, ptr, tx, bufsize);
/* Write, trigger the VM to stop, migrate, then resume. */
cmd = ahci_guest_io_halt(src, port, cmd_write,
@@ -1372,8 +1367,6 @@ static void test_flush_migrate(void)
"-device ide-hd,drive=drive0 "
"-incoming %s", tmp_path, imgfmt, uri);
- set_context(src->parent);
-
px = ahci_port_select(src);
ahci_port_clear(src, px);
@@ -1384,14 +1377,14 @@ static void test_flush_migrate(void)
cmd = ahci_command_create(CMD_FLUSH_CACHE);
ahci_command_commit(src, cmd, px);
ahci_command_issue_async(src, cmd);
- qmp_eventwait("STOP");
+ qtest_qmp_eventwait(src->parent->qts, "STOP");
/* Migrate over */
ahci_migrate(src, dst, uri);
/* Complete the command */
- qmp_send("{'execute':'cont' }");
- qmp_eventwait("RESUME");
+ qtest_qmp_send(dst->parent->qts, "{'execute':'cont' }");
+ qtest_qmp_eventwait(dst->parent->qts, "RESUME");
ahci_command_wait(dst, cmd);
ahci_command_verify(dst, cmd);
@@ -1483,7 +1476,7 @@ static int ahci_cb_cmp_buff(AHCIQState *ahci, AHCICommand *cmd,
}
rx = g_malloc0(opts->size);
- bufread(opts->buffer, rx, opts->size);
+ qtest_bufread(ahci->parent->qts, opts->buffer, rx, opts->size);
g_assert_cmphex(memcmp(tx, rx, opts->size), ==, 0);
g_free(rx);
@@ -1558,9 +1551,10 @@ static void test_atapi_bcl(void)
}
-static void atapi_wait_tray(bool open)
+static void atapi_wait_tray(AHCIQState *ahci, bool open)
{
- QDict *rsp = qmp_eventwait_ref("DEVICE_TRAY_MOVED");
+ QDict *rsp = qtest_qmp_eventwait_ref(ahci->parent->qts,
+ "DEVICE_TRAY_MOVED");
QDict *data = qdict_get_qdict(rsp, "data");
if (open) {
g_assert(qdict_get_bool(data, "tray-open"));
@@ -1587,43 +1581,46 @@ static void test_atapi_tray(void)
port = ahci_port_select(ahci);
ahci_atapi_eject(ahci, port);
- atapi_wait_tray(true);
+ atapi_wait_tray(ahci, true);
ahci_atapi_load(ahci, port);
- atapi_wait_tray(false);
+ atapi_wait_tray(ahci, false);
/* Remove media */
- qmp_send("{'execute': 'blockdev-open-tray',"
- " 'arguments': {'id': 'cd0'}}");
- atapi_wait_tray(true);
- rsp = qmp_receive();
+ qtest_qmp_send(ahci->parent->qts, "{'execute': 'blockdev-open-tray', "
+ "'arguments': {'id': 'cd0'}}");
+ atapi_wait_tray(ahci, true);
+ rsp = qtest_qmp_receive(ahci->parent->qts);
qobject_unref(rsp);
- qmp_discard_response("{'execute': 'blockdev-remove-medium', "
+ qmp_discard_response(ahci->parent->qts,
+ "{'execute': 'blockdev-remove-medium', "
"'arguments': {'id': 'cd0'}}");
/* Test the tray without a medium */
ahci_atapi_load(ahci, port);
- atapi_wait_tray(false);
+ atapi_wait_tray(ahci, false);
ahci_atapi_eject(ahci, port);
- atapi_wait_tray(true);
+ atapi_wait_tray(ahci, true);
/* Re-insert media */
- qmp_discard_response("{'execute': 'blockdev-add', "
- "'arguments': {'node-name': 'node0', "
+ qmp_discard_response(ahci->parent->qts,
+ "{'execute': 'blockdev-add', "
+ "'arguments': {'node-name': 'node0', "
"'driver': 'raw', "
"'file': { 'driver': 'file', "
"'filename': %s }}}", iso);
- qmp_discard_response("{'execute': 'blockdev-insert-medium',"
- "'arguments': { 'id': 'cd0', "
+ qmp_discard_response(ahci->parent->qts,
+ "{'execute': 'blockdev-insert-medium',"
+ "'arguments': { 'id': 'cd0', "
"'node-name': 'node0' }}");
/* Again, the event shows up first */
- qmp_send("{'execute': 'blockdev-close-tray',"
- " 'arguments': {'id': 'cd0'}}");
- atapi_wait_tray(false);
- rsp = qmp_receive();
+ qtest_qmp_send(ahci->parent->qts, "{'execute': 'blockdev-close-tray', "
+ "'arguments': {'id': 'cd0'}}");
+ atapi_wait_tray(ahci, false);
+ rsp = qtest_qmp_receive(ahci->parent->qts);
qobject_unref(rsp);
/* Now, to convince ATAPI we understand the media has changed... */
@@ -1643,10 +1640,10 @@ static void test_atapi_tray(void)
/* Final tray test. */
ahci_atapi_eject(ahci, port);
- atapi_wait_tray(true);
+ atapi_wait_tray(ahci, true);
ahci_atapi_load(ahci, port);
- atapi_wait_tray(false);
+ atapi_wait_tray(ahci, false);
/* Cleanup */
g_free(tx);
diff --git a/tests/decode/check.sh b/tests/decode/check.sh
index 79a06c37cd..95445a0115 100755
--- a/tests/decode/check.sh
+++ b/tests/decode/check.sh
@@ -15,4 +15,10 @@ for i in err_*.decode; do
fi
done
+for i in succ_*.decode; do
+ if ! $PYTHON $DECODETREE $i > /dev/null 2> /dev/null; then
+ echo FAIL:$i 1>&2
+ fi
+done
+
exit $E
diff --git a/tests/decode/err_pattern_group_empty.decode b/tests/decode/err_pattern_group_empty.decode
new file mode 100644
index 0000000000..abbff6b528
--- /dev/null
+++ b/tests/decode/err_pattern_group_empty.decode
@@ -0,0 +1,6 @@
+# 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.
+
+# empty groups are not allowed
+{
+}
diff --git a/tests/decode/err_pattern_group_ident1.decode b/tests/decode/err_pattern_group_ident1.decode
new file mode 100644
index 0000000000..3e65fab2f9
--- /dev/null
+++ b/tests/decode/err_pattern_group_ident1.decode
@@ -0,0 +1,10 @@
+# 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.
+
+%sub1 0:8
+
+# Make sure that indentation is enforced
+{
+ top 00000000 00000000 00000000 00000000
+ sub1 00000000 00000000 00000000 ........ %sub1
+}
diff --git a/tests/decode/err_pattern_group_ident2.decode b/tests/decode/err_pattern_group_ident2.decode
new file mode 100644
index 0000000000..bc859233b1
--- /dev/null
+++ b/tests/decode/err_pattern_group_ident2.decode
@@ -0,0 +1,11 @@
+# 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.
+
+%sub1 0:8
+
+# Make sure that indentation is enforced
+{
+ top 00000000 00000000 00000000 00000000
+ sub1 00000000 00000000 00000000 ........ %sub1
+# comments are suposed to be indented
+}
diff --git a/tests/decode/err_pattern_group_nest1.decode b/tests/decode/err_pattern_group_nest1.decode
new file mode 100644
index 0000000000..92e971c3c5
--- /dev/null
+++ b/tests/decode/err_pattern_group_nest1.decode
@@ -0,0 +1,13 @@
+# 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.
+
+%sub1 0:8
+%sub2 8:8
+%sub3 16:8
+%sub4 24:8
+
+# Groups with no overlap are supposed to fail
+{
+ top 00000000 00000000 00000000 00000000
+ sub4 ........ ........ ........ ........ %sub1 %sub2 %sub3 %sub4
+}
diff --git a/tests/decode/err_pattern_group_overlap1.decode b/tests/decode/err_pattern_group_overlap1.decode
new file mode 100644
index 0000000000..ebe3030d26
--- /dev/null
+++ b/tests/decode/err_pattern_group_overlap1.decode
@@ -0,0 +1,6 @@
+one 00000000000000000000000000000000
+{
+ two 0000000000000000000000000000000 s:1
+ three 000000000000000000000000000000 s:1 0
+}
+
diff --git a/tests/decode/err_width1.decode b/tests/decode/err_width1.decode
new file mode 100644
index 0000000000..0c14f6d73b
--- /dev/null
+++ b/tests/decode/err_width1.decode
@@ -0,0 +1,5 @@
+# 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.
+
+# Diagnose too many bits (33 of 32)
+one 000000000000000000000000000000000
diff --git a/tests/decode/err_width2.decode b/tests/decode/err_width2.decode
new file mode 100644
index 0000000000..47f0acf322
--- /dev/null
+++ b/tests/decode/err_width2.decode
@@ -0,0 +1,5 @@
+# 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.
+
+# Diagnose too few bits (31 of 32)
+one 0000000000000000000000000000000
diff --git a/tests/decode/err_width3.decode b/tests/decode/err_width3.decode
new file mode 100644
index 0000000000..c5fb6b3699
--- /dev/null
+++ b/tests/decode/err_width3.decode
@@ -0,0 +1,5 @@
+# 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.
+
+# Diagnose too many bits (33 of 32)
+one 0 s:32
diff --git a/tests/decode/err_width4.decode b/tests/decode/err_width4.decode
new file mode 100644
index 0000000000..1588a63698
--- /dev/null
+++ b/tests/decode/err_width4.decode
@@ -0,0 +1,5 @@
+# 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.
+
+# Diagnose too few bits (31 of 32)
+one 0 s:30
diff --git a/tests/decode/succ_pattern_group_nest1.decode b/tests/decode/succ_pattern_group_nest1.decode
new file mode 100644
index 0000000000..77b0f48b49
--- /dev/null
+++ b/tests/decode/succ_pattern_group_nest1.decode
@@ -0,0 +1,22 @@
+# 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.
+
+%sub1 0:8
+%sub2 8:8
+%sub3 16:8
+%sub4 24:7
+
+# Make sure deep netsting works, as few targets will actually exercise it
+{
+ top 00000000 00000000 00000000 00000000
+ {
+ sub1 00000000 00000000 00000000 ........ %sub1
+ {
+ sub2 00000000 00000000 ........ ........ %sub1 %sub2
+ {
+ sub3 00000000 ........ ........ ........ %sub1 %sub2 %sub3
+ sub4 0....... ........ ........ ........ %sub1 %sub2 %sub3 %sub4
+ }
+ }
+ }
+}
diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c
index 636a111a6f..d71557c5cb 100644
--- a/tests/libqos/libqos.c
+++ b/tests/libqos/libqos.c
@@ -73,11 +73,6 @@ void qtest_shutdown(QOSState *qs)
}
}
-void set_context(QOSState *s)
-{
- global_qtest = s->qts;
-}
-
static QDict *qmp_execute(QTestState *qts, const char *command)
{
return qtest_qmp(qts, "{ 'execute': %s }", command);
@@ -89,8 +84,6 @@ void migrate(QOSState *from, QOSState *to, const char *uri)
QDict *rsp, *sub;
bool running;
- set_context(from);
-
/* Is the machine currently running? */
rsp = qmp_execute(from->qts, "query-status");
g_assert(qdict_haskey(rsp, "return"));
@@ -114,7 +107,6 @@ void migrate(QOSState *from, QOSState *to, const char *uri)
/* If we were running, we can wait for an event. */
if (running) {
migrate_allocator(&from->alloc, &to->alloc);
- set_context(to);
qtest_qmp_eventwait(to->qts, "RESUME");
return;
}
@@ -144,7 +136,6 @@ void migrate(QOSState *from, QOSState *to, const char *uri)
}
migrate_allocator(&from->alloc, &to->alloc);
- set_context(to);
}
bool have_qemu_img(void)
diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h
index 149b0be8bc..8e971c25a3 100644
--- a/tests/libqos/libqos.h
+++ b/tests/libqos/libqos.h
@@ -28,7 +28,6 @@ void qtest_shutdown(QOSState *qs);
bool have_qemu_img(void);
void mkimg(const char *file, const char *fmt, unsigned size_mb);
void mkqcow2(const char *file, unsigned size_mb);
-void set_context(QOSState *s);
void migrate(QOSState *from, QOSState *to, const char *uri);
void prepare_blkdebug_script(const char *debug_fn, const char *event);
void generate_pattern(void *buffer, size_t len, size_t cycle_len);
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
index 3b50c7f188..6a3b7c2b89 100755
--- a/tests/qemu-iotests/051
+++ b/tests/qemu-iotests/051
@@ -356,6 +356,13 @@ $QEMU_IO -c "read -P 0x33 0 4k" "$TEST_IMG" | _filter_qemu_io
# Using snapshot=on with a non-existent TMPDIR
TMPDIR=/nonexistent run_qemu -drive driver=null-co,snapshot=on
+# Using snapshot=on together with read-only=on
+echo "info block" |
+ run_qemu -drive file="$TEST_IMG",snapshot=on,read-only=on,if=none,id=$device_id |
+ _filter_qemu_io |
+ sed -e 's#"/[^"]*/vl\.[A-Za-z0-9]\{6\}"#SNAPSHOT_PATH#g'
+
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index b900935fbc..9f1cf22608 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -458,4 +458,13 @@ read 4096/4096 bytes at offset 0
Testing: -drive driver=null-co,snapshot=on
QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory
+Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,read-only=on,if=none,id=drive0
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}}, "driver": "qcow2", "file": {"driver": "file", "filename": SNAPSHOT_PATH}} (qcow2, read-only)
+ Removable device: not locked, tray closed
+ Cache mode: writeback, ignore flushes
+ Backing file: TEST_DIR/t.qcow2 (chain depth: 1)
+(qemu) quit
+
*** done
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
index 8c5c735dfd..c4743cc31c 100644
--- a/tests/qemu-iotests/051.pc.out
+++ b/tests/qemu-iotests/051.pc.out
@@ -530,4 +530,13 @@ read 4096/4096 bytes at offset 0
Testing: -drive driver=null-co,snapshot=on
QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory
+Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,read-only=on,if=none,id=drive0
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) info block
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}}, "driver": "qcow2", "file": {"driver": "file", "filename": SNAPSHOT_PATH}} (qcow2, read-only)
+ Removable device: not locked, tray closed
+ Cache mode: writeback, ignore flushes
+ Backing file: TEST_DIR/t.qcow2 (chain depth: 1)
+(qemu) quit
+
*** done
diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232
index 71fd48eff0..0de097fc88 100755
--- a/tests/qemu-iotests/232
+++ b/tests/qemu-iotests/232
@@ -29,6 +29,7 @@ status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
+ rm -f $TEST_IMG.[01234]
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@@ -143,6 +144,36 @@ run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,a
run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=on
run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0
+echo
+echo "=== Try commit to backing file with auto-read-only ==="
+echo
+
+TEST_IMG="$TEST_IMG.0" _make_test_img $size
+TEST_IMG="$TEST_IMG.1" _make_test_img $size
+TEST_IMG="$TEST_IMG.2" _make_test_img $size
+TEST_IMG="$TEST_IMG.3" _make_test_img $size
+TEST_IMG="$TEST_IMG.4" _make_test_img $size
+
+(cat <<EOF
+{"execute":"qmp_capabilities"}
+{"execute":"block-commit",
+ "arguments":{"device":"format-4", "top-node": "format-2", "base-node":"format-0", "job-id":"job0"}}
+EOF
+sleep 1
+echo '{"execute":"quit"}'
+) | $QEMU -qmp stdio -nographic -nodefaults \
+ -blockdev file,node-name=file-0,filename=$TEST_IMG.0,auto-read-only=on \
+ -blockdev qcow2,node-name=format-0,file=file-0,read-only=on \
+ -blockdev file,node-name=file-1,filename=$TEST_IMG.1,auto-read-only=on \
+ -blockdev qcow2,node-name=format-1,file=file-1,read-only=on,backing=format-0 \
+ -blockdev file,node-name=file-2,filename=$TEST_IMG.2,auto-read-only=on \
+ -blockdev qcow2,node-name=format-2,file=file-2,read-only=on,backing=format-1 \
+ -blockdev file,node-name=file-3,filename=$TEST_IMG.3,auto-read-only=on \
+ -blockdev qcow2,node-name=format-3,file=file-3,read-only=on,backing=format-2 \
+ -blockdev file,node-name=file-4,filename=$TEST_IMG.4,auto-read-only=on \
+ -blockdev qcow2,node-name=format-4,file=file-4,read-only=on,backing=format-3 |
+ _filter_qmp
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/232.out b/tests/qemu-iotests/232.out
index dcb683afa3..5bcc44bb62 100644
--- a/tests/qemu-iotests/232.out
+++ b/tests/qemu-iotests/232.out
@@ -22,12 +22,12 @@ NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
=== -blockdev with read-write image: read-only/auto-read-only combinations ===
@@ -50,10 +50,30 @@ node0: TEST_DIR/t.IMGFMT (file, read-only)
node0: TEST_DIR/t.IMGFMT (file, read-only)
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
-node0: TEST_DIR/t.IMGFMT (file, read-only)
+node0: TEST_DIR/t.IMGFMT (file)
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
-node0: TEST_DIR/t.IMGFMT (file, read-only)
+node0: TEST_DIR/t.IMGFMT (file)
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
+
+=== Try commit to backing file with auto-read-only ===
+
+Formatting 'TEST_DIR/t.IMGFMT.0', fmt=IMGFMT size=134217728
+Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=134217728
+Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=134217728
+Formatting 'TEST_DIR/t.IMGFMT.3', fmt=IMGFMT size=134217728
+Formatting 'TEST_DIR/t.IMGFMT.4', fmt=IMGFMT size=134217728
+QMP_VERSION
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 134217728, "offset": 134217728, "speed": 0, "type": "commit"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
*** done
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
new file mode 100644
index 0000000000..7891a210c1
--- /dev/null
+++ b/tests/qemu-iotests/245
@@ -0,0 +1,991 @@
+#!/usr/bin/env python
+#
+# Test cases for the QMP 'x-blockdev-reopen' command
+#
+# Copyright (C) 2018-2019 Igalia, S.L.
+# Author: 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 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/>.
+#
+
+import os
+import re
+import iotests
+import copy
+import json
+from iotests import qemu_img, qemu_io
+
+hd_path = [
+ os.path.join(iotests.test_dir, 'hd0.img'),
+ os.path.join(iotests.test_dir, 'hd1.img'),
+ os.path.join(iotests.test_dir, 'hd2.img')
+]
+
+def hd_opts(idx):
+ return {'driver': iotests.imgfmt,
+ 'node-name': 'hd%d' % idx,
+ 'file': {'driver': 'file',
+ 'node-name': 'hd%d-file' % idx,
+ 'filename': hd_path[idx] } }
+
+class TestBlockdevReopen(iotests.QMPTestCase):
+ total_io_cmds = 0
+
+ def setUp(self):
+ qemu_img('create', '-f', iotests.imgfmt, hd_path[0], '3M')
+ qemu_img('create', '-f', iotests.imgfmt, '-b', hd_path[0], hd_path[1])
+ qemu_img('create', '-f', iotests.imgfmt, hd_path[2], '3M')
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa0 0 1M', hd_path[0])
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa1 1M 1M', hd_path[1])
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa2 2M 1M', hd_path[2])
+ self.vm = iotests.VM()
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ self.check_qemu_io_errors()
+ os.remove(hd_path[0])
+ os.remove(hd_path[1])
+ os.remove(hd_path[2])
+
+ # The output of qemu-io is not returned by vm.hmp_qemu_io() but
+ # it's stored in the log and can only be read when the VM has been
+ # shut down. This function runs qemu-io and keeps track of the
+ # number of times it's been called.
+ def run_qemu_io(self, img, cmd):
+ result = self.vm.hmp_qemu_io(img, cmd)
+ self.assert_qmp(result, 'return', '')
+ self.total_io_cmds += 1
+
+ # Once the VM is shut down we can parse the log and see if qemu-io
+ # ran without errors.
+ def check_qemu_io_errors(self):
+ self.assertFalse(self.vm.is_running())
+ found = 0
+ log = self.vm.get_log()
+ for line in log.split("\n"):
+ if line.startswith("Pattern verification failed"):
+ raise Exception("%s (command #%d)" % (line, found))
+ if re.match("read .*/.* bytes at offset", line):
+ found += 1
+ self.assertEqual(found, self.total_io_cmds,
+ "Expected output of %d qemu-io commands, found %d" %
+ (found, self.total_io_cmds))
+
+ # Run x-blockdev-reopen with 'opts' but applying 'newopts'
+ # on top of it. The original 'opts' dict is unmodified
+ def reopen(self, opts, newopts = {}, errmsg = None):
+ opts = copy.deepcopy(opts)
+
+ # Apply the changes from 'newopts' on top of 'opts'
+ for key in newopts:
+ value = newopts[key]
+ # If key has the form "foo.bar" then we need to do
+ # opts["foo"]["bar"] = value, not opts["foo.bar"] = value
+ subdict = opts
+ while key.find('.') != -1:
+ [prefix, key] = key.split('.', 1)
+ subdict = opts[prefix]
+ subdict[key] = value
+
+ result = self.vm.qmp('x-blockdev-reopen', conv_keys = False, **opts)
+ if errmsg:
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/desc', errmsg)
+ else:
+ self.assert_qmp(result, 'return', {})
+
+
+ # Run query-named-block-nodes and return the specified entry
+ def get_node(self, node_name):
+ result = self.vm.qmp('query-named-block-nodes')
+ for node in result['return']:
+ if node['node-name'] == node_name:
+ return node
+ return None
+
+ # Run 'query-named-block-nodes' and compare its output with the
+ # value passed by the user in 'graph'
+ def check_node_graph(self, graph):
+ result = self.vm.qmp('query-named-block-nodes')
+ self.assertEqual(json.dumps(graph, sort_keys=True),
+ json.dumps(result, sort_keys=True))
+
+ # This test opens one single disk image (without backing files)
+ # and tries to reopen it with illegal / incorrect parameters.
+ def test_incorrect_parameters_single_file(self):
+ # Open 'hd0' only (no backing files)
+ opts = hd_opts(0)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+ original_graph = self.vm.qmp('query-named-block-nodes')
+
+ # We can reopen the image passing the same options
+ self.reopen(opts)
+
+ # We can also reopen passing a child reference in 'file'
+ self.reopen(opts, {'file': 'hd0-file'})
+
+ # We cannot change any of these
+ self.reopen(opts, {'node-name': 'not-found'}, "Cannot find node named 'not-found'")
+ self.reopen(opts, {'node-name': ''}, "Cannot find node named ''")
+ self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'node-name', expected: string")
+ self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'")
+ self.reopen(opts, {'driver': ''}, "Invalid parameter ''")
+ self.reopen(opts, {'driver': None}, "Invalid parameter type for 'driver', expected: string")
+ self.reopen(opts, {'file': 'not-found'}, "Cannot change the option 'file'")
+ self.reopen(opts, {'file': ''}, "Cannot change the option 'file'")
+ self.reopen(opts, {'file': None}, "Invalid parameter type for 'file', expected: BlockdevRef")
+ self.reopen(opts, {'file.node-name': 'newname'}, "Cannot change the option 'node-name'")
+ self.reopen(opts, {'file.driver': 'host_device'}, "Cannot change the option 'driver'")
+ self.reopen(opts, {'file.filename': hd_path[1]}, "Cannot change the option 'filename'")
+ self.reopen(opts, {'file.aio': 'native'}, "Cannot change the option 'aio'")
+ self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'")
+ self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'file.filename', expected: string")
+
+ # node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it
+ del opts['node-name']
+ self.reopen(opts, {}, "Node name not specified")
+
+ # Check that nothing has changed
+ self.check_node_graph(original_graph)
+
+ # Remove the node
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
+ self.assert_qmp(result, 'return', {})
+
+ # This test opens an image with a backing file and tries to reopen
+ # it with illegal / incorrect parameters.
+ def test_incorrect_parameters_backing_file(self):
+ # Open hd1 omitting the backing options (hd0 will be opened
+ # with the default options)
+ opts = hd_opts(1)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+ original_graph = self.vm.qmp('query-named-block-nodes')
+
+ # We can't reopen the image passing the same options, 'backing' is mandatory
+ self.reopen(opts, {}, "backing is missing for 'hd1'")
+
+ # Everything works if we pass 'backing' using the existing node name
+ for node in original_graph['return']:
+ if node['drv'] == iotests.imgfmt and node['file'] == hd_path[0]:
+ backing_node_name = node['node-name']
+ self.reopen(opts, {'backing': backing_node_name})
+
+ # We can't use a non-existing or empty (non-NULL) node as the backing image
+ self.reopen(opts, {'backing': 'not-found'}, "Cannot find device= nor node_name=not-found")
+ self.reopen(opts, {'backing': ''}, "Cannot find device= nor node_name=")
+
+ # We can reopen the image just fine if we specify the backing options
+ opts['backing'] = {'driver': iotests.imgfmt,
+ 'file': {'driver': 'file',
+ 'filename': hd_path[0]}}
+ self.reopen(opts)
+
+ # We cannot change any of these options
+ self.reopen(opts, {'backing.node-name': 'newname'}, "Cannot change the option 'node-name'")
+ self.reopen(opts, {'backing.driver': 'raw'}, "Cannot change the option 'driver'")
+ self.reopen(opts, {'backing.file.node-name': 'newname'}, "Cannot change the option 'node-name'")
+ self.reopen(opts, {'backing.file.driver': 'host_device'}, "Cannot change the option 'driver'")
+
+ # Check that nothing has changed since the beginning
+ self.check_node_graph(original_graph)
+
+ # Remove the node
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1')
+ self.assert_qmp(result, 'return', {})
+
+ # Reopen an image several times changing some of its options
+ def test_reopen(self):
+ # Open the hd1 image passing all backing options
+ opts = hd_opts(1)
+ opts['backing'] = hd_opts(0)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+ original_graph = self.vm.qmp('query-named-block-nodes')
+
+ # We can reopen the image passing the same options
+ self.reopen(opts)
+
+ # Reopen in read-only mode
+ self.assert_qmp(self.get_node('hd1'), 'ro', False)
+
+ self.reopen(opts, {'read-only': True})
+ self.assert_qmp(self.get_node('hd1'), 'ro', True)
+ self.reopen(opts)
+ self.assert_qmp(self.get_node('hd1'), 'ro', False)
+
+ # Change the cache options
+ self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True)
+ self.assert_qmp(self.get_node('hd1'), 'cache/direct', False)
+ self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', False)
+ self.reopen(opts, {'cache': { 'direct': True, 'no-flush': True }})
+ self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True)
+ self.assert_qmp(self.get_node('hd1'), 'cache/direct', True)
+ self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', True)
+
+ # Reopen again with the original options
+ self.reopen(opts)
+ self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True)
+ self.assert_qmp(self.get_node('hd1'), 'cache/direct', False)
+ self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', False)
+
+ # Change 'detect-zeroes' and 'discard'
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'off')
+ self.reopen(opts, {'detect-zeroes': 'on'})
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on')
+ self.reopen(opts, {'detect-zeroes': 'unmap'},
+ "setting detect-zeroes to unmap is not allowed " +
+ "without setting discard operation to unmap")
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on')
+ self.reopen(opts, {'detect-zeroes': 'unmap', 'discard': 'unmap'})
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'unmap')
+ self.reopen(opts)
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'off')
+
+ # Changing 'force-share' is currently not supported
+ self.reopen(opts, {'force-share': True}, "Cannot change the option 'force-share'")
+
+ # Change some qcow2-specific options
+ # No way to test for success other than checking the return message
+ if iotests.imgfmt == 'qcow2':
+ self.reopen(opts, {'l2-cache-entry-size': 128 * 1024},
+ "L2 cache entry size must be a power of two "+
+ "between 512 and the cluster size (65536)")
+ self.reopen(opts, {'l2-cache-size': 1024 * 1024,
+ 'cache-size': 512 * 1024},
+ "l2-cache-size may not exceed cache-size")
+ self.reopen(opts, {'l2-cache-size': 4 * 1024 * 1024,
+ 'refcount-cache-size': 4 * 1024 * 1024,
+ 'l2-cache-entry-size': 32 * 1024})
+ self.reopen(opts, {'pass-discard-request': True})
+
+ # Check that nothing has changed since the beginning
+ # (from the parameters that we can check)
+ self.check_node_graph(original_graph)
+
+ # Check that the node names (other than the top-level one) are optional
+ del opts['file']['node-name']
+ del opts['backing']['node-name']
+ del opts['backing']['file']['node-name']
+ self.reopen(opts)
+ self.check_node_graph(original_graph)
+
+ # Reopen setting backing = null, this removes the backing image from the chain
+ self.reopen(opts, {'backing': None})
+ self.assert_qmp_absent(self.get_node('hd1'), 'image/backing-image')
+
+ # Open the 'hd0' image
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **hd_opts(0))
+ self.assert_qmp(result, 'return', {})
+
+ # Reopen the hd1 image setting 'hd0' as its backing image
+ self.reopen(opts, {'backing': 'hd0'})
+ self.assert_qmp(self.get_node('hd1'), 'image/backing-image/filename', hd_path[0])
+
+ # Check that nothing has changed since the beginning
+ self.reopen(hd_opts(0), {'read-only': True})
+ self.check_node_graph(original_graph)
+
+ # The backing file (hd0) is now a reference, we cannot change backing.* anymore
+ self.reopen(opts, {}, "Cannot change the option 'backing.driver'")
+
+ # We can't remove 'hd0' while it's a backing image of 'hd1'
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/desc', "Node 'hd0' is busy: node is used as backing hd of 'hd1'")
+
+ # But we can remove both nodes if done in the proper order
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1')
+ self.assert_qmp(result, 'return', {})
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
+ self.assert_qmp(result, 'return', {})
+
+ # Reopen a raw image and see the effect of changing the 'offset' option
+ def test_reopen_raw(self):
+ opts = {'driver': 'raw', 'node-name': 'hd0',
+ 'file': { 'driver': 'file',
+ 'filename': hd_path[0],
+ 'node-name': 'hd0-file' } }
+
+ # First we create a 2MB raw file, and fill each half with a
+ # different value
+ qemu_img('create', '-f', 'raw', hd_path[0], '2M')
+ qemu_io('-f', 'raw', '-c', 'write -P 0xa0 0 1M', hd_path[0])
+ qemu_io('-f', 'raw', '-c', 'write -P 0xa1 1M 1M', hd_path[0])
+
+ # Open the raw file with QEMU
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # Read 1MB from offset 0
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
+
+ # Reopen the image with a 1MB offset.
+ # Now the results are different
+ self.reopen(opts, {'offset': 1024*1024})
+ self.run_qemu_io("hd0", "read -P 0xa1 0 1M")
+
+ # Reopen again with the original options.
+ # We get the original results again
+ self.reopen(opts)
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
+
+ # Remove the block device
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
+ self.assert_qmp(result, 'return', {})
+
+ # Omitting an option should reset it to the default value, but if
+ # an option cannot be changed it shouldn't be possible to reset it
+ # to its default value either
+ def test_reset_default_values(self):
+ opts = {'driver': 'qcow2', 'node-name': 'hd0',
+ 'file': { 'driver': 'file',
+ 'filename': hd_path[0],
+ 'x-check-cache-dropped': True, # This one can be changed
+ 'locking': 'off', # This one can NOT be changed
+ 'node-name': 'hd0-file' } }
+
+ # Open the file with QEMU
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # file.x-check-cache-dropped can be changed...
+ self.reopen(opts, { 'file.x-check-cache-dropped': False })
+ # ...and dropped completely (resetting to the default value)
+ del opts['file']['x-check-cache-dropped']
+ self.reopen(opts)
+
+ # file.locking cannot be changed nor reset to the default value
+ self.reopen(opts, { 'file.locking': 'on' }, "Cannot change the option 'locking'")
+ del opts['file']['locking']
+ self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value")
+ # But we can reopen it if we maintain its previous value
+ self.reopen(opts, { 'file.locking': 'off' })
+
+ # Remove the block device
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
+ self.assert_qmp(result, 'return', {})
+
+ # This test modifies the node graph a few times by changing the
+ # 'backing' option on reopen and verifies that the guest data that
+ # is read afterwards is consistent with the graph changes.
+ def test_io_with_graph_changes(self):
+ opts = []
+
+ # Open hd0, hd1 and hd2 without any backing image
+ for i in range(3):
+ opts.append(hd_opts(i))
+ opts[i]['backing'] = None
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i])
+ self.assert_qmp(result, 'return', {})
+
+ # hd0
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
+ self.run_qemu_io("hd0", "read -P 0 1M 1M")
+ self.run_qemu_io("hd0", "read -P 0 2M 1M")
+
+ # hd1 <- hd0
+ self.reopen(opts[0], {'backing': 'hd1'})
+
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
+ self.run_qemu_io("hd0", "read -P 0xa1 1M 1M")
+ self.run_qemu_io("hd0", "read -P 0 2M 1M")
+
+ # hd1 <- hd0 , hd1 <- hd2
+ self.reopen(opts[2], {'backing': 'hd1'})
+
+ self.run_qemu_io("hd2", "read -P 0 0 1M")
+ self.run_qemu_io("hd2", "read -P 0xa1 1M 1M")
+ self.run_qemu_io("hd2", "read -P 0xa2 2M 1M")
+
+ # hd1 <- hd2 <- hd0
+ self.reopen(opts[0], {'backing': 'hd2'})
+
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
+ self.run_qemu_io("hd0", "read -P 0xa1 1M 1M")
+ self.run_qemu_io("hd0", "read -P 0xa2 2M 1M")
+
+ # hd2 <- hd0
+ self.reopen(opts[2], {'backing': None})
+
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
+ self.run_qemu_io("hd0", "read -P 0 1M 1M")
+ self.run_qemu_io("hd0", "read -P 0xa2 2M 1M")
+
+ # hd2 <- hd1 <- hd0
+ self.reopen(opts[1], {'backing': 'hd2'})
+ self.reopen(opts[0], {'backing': 'hd1'})
+
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
+ self.run_qemu_io("hd0", "read -P 0xa1 1M 1M")
+ self.run_qemu_io("hd0", "read -P 0xa2 2M 1M")
+
+ # Illegal operation: hd2 is a child of hd1
+ self.reopen(opts[2], {'backing': 'hd1'},
+ "Making 'hd1' a backing file of 'hd2' would create a cycle")
+
+ # hd2 <- hd0, hd2 <- hd1
+ self.reopen(opts[0], {'backing': 'hd2'})
+
+ self.run_qemu_io("hd1", "read -P 0 0 1M")
+ self.run_qemu_io("hd1", "read -P 0xa1 1M 1M")
+ self.run_qemu_io("hd1", "read -P 0xa2 2M 1M")
+
+ # More illegal operations
+ self.reopen(opts[2], {'backing': 'hd1'},
+ "Making 'hd1' a backing file of 'hd2' would create a cycle")
+ self.reopen(opts[2], {'file': 'hd0-file'}, "Cannot change the option 'file'")
+
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/desc', "Node 'hd2' is busy: node is used as backing hd of 'hd0'")
+
+ # hd1 doesn't have a backing file now
+ self.reopen(opts[1], {'backing': None})
+
+ self.run_qemu_io("hd1", "read -P 0 0 1M")
+ self.run_qemu_io("hd1", "read -P 0xa1 1M 1M")
+ self.run_qemu_io("hd1", "read -P 0 2M 1M")
+
+ # We can't remove the 'backing' option if the image has a
+ # default backing file
+ del opts[1]['backing']
+ self.reopen(opts[1], {}, "backing is missing for 'hd1'")
+
+ self.run_qemu_io("hd1", "read -P 0 0 1M")
+ self.run_qemu_io("hd1", "read -P 0xa1 1M 1M")
+ self.run_qemu_io("hd1", "read -P 0 2M 1M")
+
+ # This test verifies that we can't change the children of a block
+ # device during a reopen operation in a way that would create
+ # cycles in the node graph
+ def test_graph_cycles(self):
+ opts = []
+
+ # Open all three images without backing file
+ for i in range(3):
+ opts.append(hd_opts(i))
+ opts[i]['backing'] = None
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i])
+ self.assert_qmp(result, 'return', {})
+
+ # hd1 <- hd0, hd1 <- hd2
+ self.reopen(opts[0], {'backing': 'hd1'})
+ self.reopen(opts[2], {'backing': 'hd1'})
+
+ # Illegal: hd2 is backed by hd1
+ self.reopen(opts[1], {'backing': 'hd2'},
+ "Making 'hd2' a backing file of 'hd1' would create a cycle")
+
+ # hd1 <- hd0 <- hd2
+ self.reopen(opts[2], {'backing': 'hd0'})
+
+ # Illegal: hd2 is backed by hd0, which is backed by hd1
+ self.reopen(opts[1], {'backing': 'hd2'},
+ "Making 'hd2' a backing file of 'hd1' would create a cycle")
+
+ # Illegal: hd1 cannot point to itself
+ self.reopen(opts[1], {'backing': 'hd1'},
+ "Making 'hd1' a backing file of 'hd1' would create a cycle")
+
+ # Remove all backing files
+ self.reopen(opts[0])
+ self.reopen(opts[2])
+
+ ##########################################
+ # Add a blkverify node using hd0 and hd1 #
+ ##########################################
+ bvopts = {'driver': 'blkverify',
+ 'node-name': 'bv',
+ 'test': 'hd0',
+ 'raw': 'hd1'}
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **bvopts)
+ self.assert_qmp(result, 'return', {})
+
+ # blkverify doesn't currently allow reopening. TODO: implement this
+ self.reopen(bvopts, {}, "Block format 'blkverify' used by node 'bv'" +
+ " does not support reopening files")
+
+ # Illegal: hd0 is a child of the blkverify node
+ self.reopen(opts[0], {'backing': 'bv'},
+ "Making 'bv' a backing file of 'hd0' would create a cycle")
+
+ # Delete the blkverify node
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bv')
+ self.assert_qmp(result, 'return', {})
+
+ # Misc reopen tests with different block drivers
+ def test_misc_drivers(self):
+ ####################
+ ###### quorum ######
+ ####################
+ for i in range(3):
+ opts = hd_opts(i)
+ # Open all three images without backing file
+ opts['backing'] = None
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ opts = {'driver': 'quorum',
+ 'node-name': 'quorum0',
+ 'children': ['hd0', 'hd1', 'hd2'],
+ 'vote-threshold': 2}
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # Quorum doesn't currently allow reopening. TODO: implement this
+ self.reopen(opts, {}, "Block format 'quorum' used by node 'quorum0'" +
+ " does not support reopening files")
+
+ # You can't make quorum0 a backing file of hd0:
+ # hd0 is already a child of quorum0.
+ self.reopen(hd_opts(0), {'backing': 'quorum0'},
+ "Making 'quorum0' a backing file of 'hd0' would create a cycle")
+
+ # Delete quorum0
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'quorum0')
+ self.assert_qmp(result, 'return', {})
+
+ # Delete hd0, hd1 and hd2
+ for i in range(3):
+ result = self.vm.qmp('blockdev-del', conv_keys = True,
+ node_name = 'hd%d' % i)
+ self.assert_qmp(result, 'return', {})
+
+ ######################
+ ###### blkdebug ######
+ ######################
+ opts = {'driver': 'blkdebug',
+ 'node-name': 'bd',
+ 'config': '/dev/null',
+ 'image': hd_opts(0)}
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # blkdebug allows reopening if we keep the same options
+ self.reopen(opts)
+
+ # but it currently does not allow changes
+ self.reopen(opts, {'image': 'hd1'}, "Cannot change the option 'image'")
+ self.reopen(opts, {'align': 33554432}, "Cannot change the option 'align'")
+ self.reopen(opts, {'config': '/non/existent'}, "Cannot change the option 'config'")
+ del opts['config']
+ self.reopen(opts, {}, "Option 'config' cannot be reset to its default value")
+
+ # Delete the blkdebug node
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bd')
+ self.assert_qmp(result, 'return', {})
+
+ ##################
+ ###### null ######
+ ##################
+ opts = {'driver': 'null-aio', 'node-name': 'root', 'size': 1024}
+
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # 1 << 30 is the default value, but we cannot change it explicitly
+ self.reopen(opts, {'size': (1 << 30)}, "Cannot change the option 'size'")
+
+ # We cannot change 'size' back to its default value either
+ del opts['size']
+ self.reopen(opts, {}, "Option 'size' cannot be reset to its default value")
+
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'root')
+ self.assert_qmp(result, 'return', {})
+
+ ##################
+ ###### file ######
+ ##################
+ opts = hd_opts(0)
+ opts['file']['locking'] = 'on'
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # 'locking' cannot be changed
+ del opts['file']['locking']
+ self.reopen(opts, {'file.locking': 'on'})
+ self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'")
+ self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value")
+
+ # Trying to reopen the 'file' node directly does not make a difference
+ opts = opts['file']
+ self.reopen(opts, {'locking': 'on'})
+ self.reopen(opts, {'locking': 'off'}, "Cannot change the option 'locking'")
+ self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value")
+
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
+ self.assert_qmp(result, 'return', {})
+
+ ######################
+ ###### throttle ######
+ ######################
+ opts = { 'qom-type': 'throttle-group', 'id': 'group0',
+ 'props': { 'limits': { 'iops-total': 1000 } } }
+ result = self.vm.qmp('object-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ opts = { 'qom-type': 'throttle-group', 'id': 'group1',
+ 'props': { 'limits': { 'iops-total': 2000 } } }
+ result = self.vm.qmp('object-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # Add a throttle filter with group = group0
+ opts = { 'driver': 'throttle', 'node-name': 'throttle0',
+ 'throttle-group': 'group0', 'file': hd_opts(0) }
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # We can reopen it if we keep the same options
+ self.reopen(opts)
+
+ # We can also reopen if 'file' is a reference to the child
+ self.reopen(opts, {'file': 'hd0'})
+
+ # This is illegal
+ self.reopen(opts, {'throttle-group': 'notfound'}, "Throttle group 'notfound' does not exist")
+
+ # But it's possible to change the group to group1
+ self.reopen(opts, {'throttle-group': 'group1'})
+
+ # Now group1 is in use, it cannot be deleted
+ result = self.vm.qmp('object-del', id = 'group1')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/desc', "object 'group1' is in use, can not be deleted")
+
+ # Default options, this switches the group back to group0
+ self.reopen(opts)
+
+ # So now we cannot delete group0
+ result = self.vm.qmp('object-del', id = 'group0')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/desc', "object 'group0' is in use, can not be deleted")
+
+ # But group1 is free this time, and it can be deleted
+ result = self.vm.qmp('object-del', id = 'group1')
+ self.assert_qmp(result, 'return', {})
+
+ # Let's delete the filter node
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'throttle0')
+ self.assert_qmp(result, 'return', {})
+
+ # And we can finally get rid of group0
+ result = self.vm.qmp('object-del', id = 'group0')
+ self.assert_qmp(result, 'return', {})
+
+ # If an image has a backing file then the 'backing' option must be
+ # passed on reopen. We don't allow leaving the option out in this
+ # case because it's unclear what the correct semantics would be.
+ def test_missing_backing_options_1(self):
+ # hd2
+ opts = hd_opts(2)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # hd0
+ opts = hd_opts(0)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # hd0 has no backing file: we can omit the 'backing' option
+ self.reopen(opts)
+
+ # hd2 <- hd0
+ self.reopen(opts, {'backing': 'hd2'})
+
+ # hd0 has a backing file: we must set the 'backing' option
+ self.reopen(opts, {}, "backing is missing for 'hd0'")
+
+ # hd2 can't be removed because it's the backing file of hd0
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.assert_qmp(result, 'error/desc', "Node 'hd2' is busy: node is used as backing hd of 'hd0'")
+
+ # Detach hd2 from hd0.
+ self.reopen(opts, {'backing': None})
+ self.reopen(opts, {}, "backing is missing for 'hd0'")
+
+ # Remove both hd0 and hd2
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2')
+ self.assert_qmp(result, 'return', {})
+
+ # If an image has default backing file (as part of its metadata)
+ # then the 'backing' option must be passed on reopen. We don't
+ # allow leaving the option out in this case because it's unclear
+ # what the correct semantics would be.
+ def test_missing_backing_options_2(self):
+ # hd0 <- hd1
+ # (hd0 is hd1's default backing file)
+ opts = hd_opts(1)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # hd1 has a backing file: we can't omit the 'backing' option
+ self.reopen(opts, {}, "backing is missing for 'hd1'")
+
+ # Let's detach the backing file
+ self.reopen(opts, {'backing': None})
+
+ # No backing file attached to hd1 now, but we still can't omit the 'backing' option
+ self.reopen(opts, {}, "backing is missing for 'hd1'")
+
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1')
+ self.assert_qmp(result, 'return', {})
+
+ # Test that making 'backing' a reference to an existing child
+ # keeps its current options
+ def test_backing_reference(self):
+ # hd2 <- hd1 <- hd0
+ opts = hd_opts(0)
+ opts['backing'] = hd_opts(1)
+ opts['backing']['backing'] = hd_opts(2)
+ # Enable 'detect-zeroes' on all three nodes
+ opts['detect-zeroes'] = 'on'
+ opts['backing']['detect-zeroes'] = 'on'
+ opts['backing']['backing']['detect-zeroes'] = 'on'
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # Reopen the chain passing the minimum amount of required options.
+ # By making 'backing' a reference to hd1 (instead of a sub-dict)
+ # we tell QEMU to keep its current set of options.
+ opts = {'driver': iotests.imgfmt,
+ 'node-name': 'hd0',
+ 'file': 'hd0-file',
+ 'backing': 'hd1' }
+ self.reopen(opts)
+
+ # This has reset 'detect-zeroes' on hd0, but not on hd1 and hd2.
+ self.assert_qmp(self.get_node('hd0'), 'detect_zeroes', 'off')
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on')
+ self.assert_qmp(self.get_node('hd2'), 'detect_zeroes', 'on')
+
+ # Test what happens if the graph changes due to other operations
+ # such as block-stream
+ def test_block_stream_1(self):
+ # hd1 <- hd0
+ opts = hd_opts(0)
+ opts['backing'] = hd_opts(1)
+ opts['backing']['backing'] = None
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # Stream hd1 into hd0 and wait until it's done
+ result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', device = 'hd0')
+ self.assert_qmp(result, 'return', {})
+ self.wait_until_completed(drive = 'stream0')
+
+ # Now we have only hd0
+ self.assertEqual(self.get_node('hd1'), None)
+
+ # We have backing.* options but there's no backing file anymore
+ self.reopen(opts, {}, "Cannot change the option 'backing.driver'")
+
+ # If we remove the 'backing' option then we can reopen hd0 just fine
+ del opts['backing']
+ self.reopen(opts)
+
+ # We can also reopen hd0 if we set 'backing' to null
+ self.reopen(opts, {'backing': None})
+
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
+ self.assert_qmp(result, 'return', {})
+
+ # Another block_stream test
+ def test_block_stream_2(self):
+ # hd2 <- hd1 <- hd0
+ opts = hd_opts(0)
+ opts['backing'] = hd_opts(1)
+ opts['backing']['backing'] = hd_opts(2)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # Stream hd1 into hd0 and wait until it's done
+ result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0',
+ device = 'hd0', base_node = 'hd2')
+ self.assert_qmp(result, 'return', {})
+ self.wait_until_completed(drive = 'stream0')
+
+ # The chain is hd2 <- hd0 now. hd1 is missing
+ self.assertEqual(self.get_node('hd1'), None)
+
+ # The backing options in the dict were meant for hd1, but we cannot
+ # use them with hd2 because hd1 had a backing file while hd2 does not.
+ self.reopen(opts, {}, "Cannot change the option 'backing.driver'")
+
+ # If we remove hd1's options from the dict then things work fine
+ opts['backing'] = opts['backing']['backing']
+ self.reopen(opts)
+
+ # We can also reopen hd0 if we use a reference to the backing file
+ self.reopen(opts, {'backing': 'hd2'})
+
+ # But we cannot leave the option out
+ del opts['backing']
+ self.reopen(opts, {}, "backing is missing for 'hd0'")
+
+ # Now we can delete hd0 (and hd2)
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
+ self.assert_qmp(result, 'return', {})
+ self.assertEqual(self.get_node('hd2'), None)
+
+ # Reopen the chain during a block-stream job (from hd1 to hd0)
+ def test_block_stream_3(self):
+ # hd2 <- hd1 <- hd0
+ opts = hd_opts(0)
+ opts['backing'] = hd_opts(1)
+ opts['backing']['backing'] = hd_opts(2)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # hd2 <- hd0
+ result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0',
+ device = 'hd0', base_node = 'hd2', speed = 512 * 1024)
+ self.assert_qmp(result, 'return', {})
+
+ # We can't remove hd2 while the stream job is ongoing
+ opts['backing']['backing'] = None
+ self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
+
+ # We can't remove hd1 while the stream job is ongoing
+ opts['backing'] = None
+ self.reopen(opts, {}, "Cannot change 'backing' link from 'hd0' to 'hd1'")
+
+ self.wait_until_completed(drive = 'stream0')
+
+ # Reopen the chain during a block-stream job (from hd2 to hd1)
+ def test_block_stream_4(self):
+ # hd2 <- hd1 <- hd0
+ opts = hd_opts(0)
+ opts['backing'] = hd_opts(1)
+ opts['backing']['backing'] = hd_opts(2)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ # hd1 <- hd0
+ result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0',
+ device = 'hd1', speed = 512 * 1024)
+ self.assert_qmp(result, 'return', {})
+
+ # We can't reopen with the original options because that would
+ # make hd1 read-only and block-stream requires it to be read-write
+ self.reopen(opts, {}, "Can't set node 'hd1' to r/o with copy-on-read enabled")
+
+ # We can't remove hd2 while the stream job is ongoing
+ opts['backing']['backing'] = None
+ self.reopen(opts, {'backing.read-only': False}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
+
+ # We can detach hd1 from hd0 because it doesn't affect the stream job
+ opts['backing'] = None
+ self.reopen(opts)
+
+ self.wait_until_completed(drive = 'stream0')
+
+ # Reopen the chain during a block-commit job (from hd0 to hd2)
+ def test_block_commit_1(self):
+ # hd2 <- hd1 <- hd0
+ opts = hd_opts(0)
+ opts['backing'] = hd_opts(1)
+ opts['backing']['backing'] = hd_opts(2)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('block-commit', conv_keys = True, job_id = 'commit0',
+ device = 'hd0', speed = 1024 * 1024)
+ self.assert_qmp(result, 'return', {})
+
+ # We can't remove hd2 while the commit job is ongoing
+ opts['backing']['backing'] = None
+ self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
+
+ # We can't remove hd1 while the commit job is ongoing
+ opts['backing'] = None
+ self.reopen(opts, {}, "Cannot change 'backing' link from 'hd0' to 'hd1'")
+
+ event = self.vm.event_wait(name='BLOCK_JOB_READY')
+ self.assert_qmp(event, 'data/device', 'commit0')
+ self.assert_qmp(event, 'data/type', 'commit')
+ self.assert_qmp_absent(event, 'data/error')
+
+ result = self.vm.qmp('block-job-complete', device='commit0')
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_until_completed(drive = 'commit0')
+
+ # Reopen the chain during a block-commit job (from hd1 to hd2)
+ def test_block_commit_2(self):
+ # hd2 <- hd1 <- hd0
+ opts = hd_opts(0)
+ opts['backing'] = hd_opts(1)
+ opts['backing']['backing'] = hd_opts(2)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('block-commit', conv_keys = True, job_id = 'commit0',
+ device = 'hd0', top_node = 'hd1', speed = 1024 * 1024)
+ self.assert_qmp(result, 'return', {})
+
+ # We can't remove hd2 while the commit job is ongoing
+ opts['backing']['backing'] = None
+ self.reopen(opts, {}, "Cannot change the option 'backing.driver'")
+
+ # We can't remove hd1 while the commit job is ongoing
+ opts['backing'] = None
+ self.reopen(opts, {}, "Cannot change backing link if 'hd0' has an implicit backing file")
+
+ # hd2 <- hd0
+ self.wait_until_completed(drive = 'commit0')
+
+ self.assert_qmp(self.get_node('hd0'), 'ro', False)
+ self.assertEqual(self.get_node('hd1'), None)
+ self.assert_qmp(self.get_node('hd2'), 'ro', True)
+
+ # We don't allow setting a backing file that uses a different AioContext
+ def test_iothreads(self):
+ opts = hd_opts(0)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
+ self.assert_qmp(result, 'return', {})
+
+ opts2 = hd_opts(2)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts2)
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('object-add', qom_type='iothread', id='iothread0')
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('object-add', qom_type='iothread', id='iothread1')
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd0', iothread='iothread0')
+ self.assert_qmp(result, 'return', {})
+
+ self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext")
+
+ result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread1')
+ self.assert_qmp(result, 'return', {})
+
+ self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext")
+
+ result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread0')
+ self.assert_qmp(result, 'return', {})
+
+ self.reopen(opts, {'backing': 'hd2'})
+
+if __name__ == '__main__':
+ iotests.main(supported_fmts=["qcow2"])
diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out
new file mode 100644
index 0000000000..71009c239f
--- /dev/null
+++ b/tests/qemu-iotests/245.out
@@ -0,0 +1,5 @@
+..................
+----------------------------------------------------------------------
+Ran 18 tests
+
+OK
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index b97d83331b..7289309604 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -243,4 +243,5 @@
242 rw auto quick
243 rw auto quick
244 rw auto quick
+245 rw auto
246 rw auto quick
diff --git a/tests/test-announce-self.c b/tests/test-announce-self.c
index 1644d34a3f..0e6d466aa4 100644
--- a/tests/test-announce-self.c
+++ b/tests/test-announce-self.c
@@ -21,18 +21,8 @@
#define ETH_P_RARP 0x8035
#endif
-static QTestState *test_init(int socket)
-{
- char *args;
-
- args = g_strdup_printf("-netdev socket,fd=%d,id=hs0 -device "
- "virtio-net-pci,netdev=hs0", socket);
-
- return qtest_start(args);
-}
-
-static void test_announce(int socket)
+static void test_announce(QTestState *qs, int socket)
{
char buffer[60];
int len;
@@ -40,7 +30,7 @@ static void test_announce(int socket)
int ret;
uint16_t *proto = (uint16_t *)&buffer[12];
- rsp = qmp("{ 'execute' : 'announce-self', "
+ rsp = qtest_qmp(qs, "{ 'execute' : 'announce-self', "
" 'arguments': {"
" 'initial': 50, 'max': 550,"
" 'rounds': 10, 'step': 50 } }");
@@ -59,14 +49,15 @@ static void test_announce(int socket)
static void setup(gconstpointer data)
{
QTestState *qs;
- void (*func) (int socket) = data;
+ void (*func) (QTestState *qs, int socket) = data;
int sv[2], ret;
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);
g_assert_cmpint(ret, !=, -1);
- qs = test_init(sv[1]);
- func(sv[0]);
+ qs = qtest_initf("-netdev socket,fd=%d,id=hs0 -device "
+ "virtio-net-pci,netdev=hs0", sv[1]);
+ func(qs, sv[0]);
/* End test */
close(sv[0]);
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index 0c965b3b1e..3817966010 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -178,7 +178,8 @@ static void append_mem_opts(TestServer *server, GString *cmd_line,
int size, enum test_memfd memfd)
{
if (memfd == TEST_MEMFD_AUTO) {
- memfd = qemu_memfd_check(0) ? TEST_MEMFD_YES : TEST_MEMFD_NO;
+ memfd = qemu_memfd_check(MFD_ALLOW_SEALING) ? TEST_MEMFD_YES
+ : TEST_MEMFD_NO;
}
if (memfd == TEST_MEMFD_YES) {
@@ -930,7 +931,7 @@ static void register_vhost_user_test(void)
"virtio-net",
test_read_guest_mem, &opts);
- if (qemu_memfd_check(0)) {
+ if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
opts.before = vhost_user_test_setup_memfd;
qos_add_test("vhost-user/read-guest-mem/memfd",
"virtio-net",
diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c
index b02be0274e..b65365934b 100644
--- a/tests/virtio-blk-test.c
+++ b/tests/virtio-blk-test.c
@@ -751,7 +751,7 @@ static void *virtio_blk_test_setup(GString *cmd_line, void *arg)
char *tmp_path = drive_create();
g_string_append_printf(cmd_line,
- " -drive if=none,id=drive0,file=%s,format=raw "
+ " -drive if=none,id=drive0,file=%s,format=raw,auto-read-only=off "
"-drive if=none,id=drive1,file=null-co://,format=raw ",
tmp_path);
diff --git a/ui/vnc.c b/ui/vnc.c
index 2d9e8f43b0..1871422e1d 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1019,16 +1019,16 @@ static void vnc_update_throttle_offset(VncState *vs)
int bps;
switch (vs->as.fmt) {
default:
- case AUD_FMT_U8:
- case AUD_FMT_S8:
+ case AUDIO_FORMAT_U8:
+ case AUDIO_FORMAT_S8:
bps = 1;
break;
- case AUD_FMT_U16:
- case AUD_FMT_S16:
+ case AUDIO_FORMAT_U16:
+ case AUDIO_FORMAT_S16:
bps = 2;
break;
- case AUD_FMT_U32:
- case AUD_FMT_S32:
+ case AUDIO_FORMAT_U32:
+ case AUDIO_FORMAT_S32:
bps = 4;
break;
}
@@ -2375,12 +2375,12 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
if (len == 4)
return 10;
switch (read_u8(data, 4)) {
- case 0: vs->as.fmt = AUD_FMT_U8; break;
- case 1: vs->as.fmt = AUD_FMT_S8; break;
- case 2: vs->as.fmt = AUD_FMT_U16; break;
- case 3: vs->as.fmt = AUD_FMT_S16; break;
- case 4: vs->as.fmt = AUD_FMT_U32; break;
- case 5: vs->as.fmt = AUD_FMT_S32; break;
+ case 0: vs->as.fmt = AUDIO_FORMAT_U8; break;
+ case 1: vs->as.fmt = AUDIO_FORMAT_S8; break;
+ case 2: vs->as.fmt = AUDIO_FORMAT_U16; break;
+ case 3: vs->as.fmt = AUDIO_FORMAT_S16; break;
+ case 4: vs->as.fmt = AUDIO_FORMAT_U32; break;
+ case 5: vs->as.fmt = AUDIO_FORMAT_S32; break;
default:
VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4));
vnc_client_error(vs);
@@ -3111,7 +3111,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
vs->as.freq = 44100;
vs->as.nchannels = 2;
- vs->as.fmt = AUD_FMT_S16;
+ vs->as.fmt = AUDIO_FORMAT_S16;
vs->as.endianness = 0;
qemu_mutex_init(&vs->output_mutex);
diff --git a/util/memfd.c b/util/memfd.c
index 8debd0d037..00334e5b21 100644
--- a/util/memfd.c
+++ b/util/memfd.c
@@ -40,6 +40,7 @@ static int memfd_create(const char *name, unsigned int flags)
#ifdef __NR_memfd_create
return syscall(__NR_memfd_create, name, flags);
#else
+ errno = ENOSYS;
return -1;
#endif
}
@@ -70,14 +71,18 @@ int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
}
mfd = memfd_create(name, flags);
if (mfd < 0) {
+ error_setg_errno(errp, errno,
+ "failed to create memfd with flags 0x%x", flags);
goto err;
}
if (ftruncate(mfd, size) == -1) {
+ error_setg_errno(errp, errno, "failed to resize memfd to %zu", size);
goto err;
}
if (seals && fcntl(mfd, F_ADD_SEALS, seals) == -1) {
+ error_setg_errno(errp, errno, "failed to add seals 0x%x", seals);
goto err;
}
@@ -87,8 +92,9 @@ err:
if (mfd >= 0) {
close(mfd);
}
+#else
+ error_setg_errno(errp, ENOSYS, "failed to create memfd");
#endif
- error_setg_errno(errp, errno, "failed to create memfd");
return -1;
}
@@ -188,7 +194,7 @@ bool qemu_memfd_alloc_check(void)
bool qemu_memfd_check(unsigned int flags)
{
#ifdef CONFIG_LINUX
- int mfd = memfd_create("test", flags);
+ int mfd = memfd_create("test", flags | MFD_CLOEXEC);
if (mfd >= 0) {
close(mfd);
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index 326d92dcd2..88dda9cd39 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -512,6 +512,59 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
}
}
+uint64_t qemu_get_pmem_size(const char *filename, Error **errp)
+{
+ struct stat st;
+
+ if (stat(filename, &st) < 0) {
+ error_setg(errp, "unable to stat pmem file \"%s\"", filename);
+ return 0;
+ }
+
+#if defined(__linux__)
+ /* Special handling for devdax character devices */
+ if (S_ISCHR(st.st_mode)) {
+ char *subsystem_path = NULL;
+ char *subsystem = NULL;
+ char *size_path = NULL;
+ char *size_str = NULL;
+ uint64_t ret = 0;
+
+ subsystem_path = g_strdup_printf("/sys/dev/char/%d:%d/subsystem",
+ major(st.st_rdev), minor(st.st_rdev));
+ subsystem = g_file_read_link(subsystem_path, NULL);
+ if (!subsystem) {
+ error_setg(errp, "unable to read subsystem for pmem file \"%s\"",
+ filename);
+ goto devdax_err;
+ }
+
+ if (!g_str_has_suffix(subsystem, "/dax")) {
+ error_setg(errp, "pmem file \"%s\" is not a dax device", filename);
+ goto devdax_err;
+ }
+
+ size_path = g_strdup_printf("/sys/dev/char/%d:%d/size",
+ major(st.st_rdev), minor(st.st_rdev));
+ if (!g_file_get_contents(size_path, &size_str, NULL, NULL)) {
+ error_setg(errp, "unable to read size for pmem file \"%s\"",
+ size_path);
+ goto devdax_err;
+ }
+
+ ret = g_ascii_strtoull(size_str, NULL, 0);
+
+devdax_err:
+ g_free(size_str);
+ g_free(size_path);
+ g_free(subsystem);
+ g_free(subsystem_path);
+ return ret;
+ }
+#endif /* defined(__linux__) */
+
+ return st.st_size;
+}
char *qemu_get_pid_name(pid_t pid)
{
diff --git a/util/oslib-win32.c b/util/oslib-win32.c
index b4c17f5dfa..bd633afab6 100644
--- a/util/oslib-win32.c
+++ b/util/oslib-win32.c
@@ -560,6 +560,11 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus,
}
}
+uint64_t qemu_get_pmem_size(const char *filename, Error **errp)
+{
+ error_setg(errp, "pmem support not available");
+ return 0;
+}
char *qemu_get_pid_name(pid_t pid)
{
diff --git a/vl.c b/vl.c
index 5616208ea0..027b853d92 100644
--- a/vl.c
+++ b/vl.c
@@ -3285,9 +3285,12 @@ int main(int argc, char **argv, char **envp)
add_device_config(DEV_BT, optarg);
break;
case QEMU_OPTION_audio_help:
- AUD_help ();
+ audio_legacy_help();
exit (0);
break;
+ case QEMU_OPTION_audiodev:
+ audio_parse_option(optarg);
+ break;
case QEMU_OPTION_soundhw:
select_soundhw (optarg);
break;
@@ -4454,6 +4457,8 @@ int main(int argc, char **argv, char **envp)
/* do monitor/qmp handling at preconfig state if requested */
main_loop();
+ audio_init_audiodevs();
+
/* from here on runstate is RUN_STATE_PRELAUNCH */
machine_run_board_init(current_machine);