#include "sysemu/iothread.h"
#include "block/block_int.h"
#include "block/trace.h"
-#include "sysemu/arch_init.h"
#include "sysemu/runstate.h"
#include "sysemu/replay.h"
#include "qemu/cutils.h"
#include "qemu/main-loop.h"
#include "qemu/throttle-options.h"
+/* Protected by BQL */
QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states);
void bdrv_set_monitor_owned(BlockDriverState *bs)
{
+ GLOBAL_STATE_CODE();
QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list);
}
BlockBackend *blk;
DriveInfo *dinfo;
+ GLOBAL_STATE_CODE();
+
if (max_devs <= 0) {
return;
}
DriveInfo *dinfo = blk_legacy_dinfo(blk);
BlockJob *job;
+ GLOBAL_STATE_CODE();
+
if (!dinfo) {
return;
}
- for (job = block_job_next(NULL); job; job = block_job_next(job)) {
- if (block_job_has_bdrv(job, blk_bs(blk))) {
- AioContext *aio_context = job->job.aio_context;
- aio_context_acquire(aio_context);
-
- job_cancel(&job->job, false);
+ JOB_LOCK_GUARD();
- aio_context_release(aio_context);
+ for (job = block_job_next_locked(NULL); job;
+ job = block_job_next_locked(job)) {
+ if (block_job_has_bdrv(job, blk_bs(blk))) {
+ job_cancel_locked(&job->job, false);
}
}
void blockdev_auto_del(BlockBackend *blk)
{
DriveInfo *dinfo = blk_legacy_dinfo(blk);
+ GLOBAL_STATE_CODE();
if (dinfo && dinfo->auto_del) {
monitor_remove_blk(blk);
}
}
-/**
- * Returns the current mapping of how many units per bus
- * a particular interface can support.
- *
- * A positive integer indicates n units per bus.
- * 0 implies the mapping has not been established.
- * -1 indicates an invalid BlockInterfaceType was given.
- */
-int drive_get_max_devs(BlockInterfaceType type)
-{
- if (type >= IF_IDE && type < IF_COUNT) {
- return if_max_devs[type];
- }
-
- return -1;
-}
-
static int drive_index_to_bus_id(BlockInterfaceType type, int index)
{
int max_devs = if_max_devs[type];
return max_devs ? index % max_devs : index;
}
-QemuOpts *drive_def(const char *optstr)
-{
- return qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
-}
-
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
const char *optstr)
{
QemuOpts *opts;
- opts = drive_def(optstr);
+ GLOBAL_STATE_CODE();
+
+ opts = qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
if (!opts) {
return NULL;
}
BlockBackend *blk;
DriveInfo *dinfo;
+ GLOBAL_STATE_CODE();
+
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
dinfo = blk_legacy_dinfo(blk);
if (dinfo && dinfo->type == type
Location loc;
bool orphans = false;
+ GLOBAL_STATE_CODE();
+
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
dinfo = blk_legacy_dinfo(blk);
/*
DriveInfo *drive_get_by_index(BlockInterfaceType type, int index)
{
+ GLOBAL_STATE_CODE();
return drive_get(type,
drive_index_to_bus_id(type, index),
drive_index_to_unit_id(type, index));
BlockBackend *blk;
DriveInfo *dinfo;
+ GLOBAL_STATE_CODE();
+
max_bus = -1;
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
dinfo = blk_legacy_dinfo(blk);
return max_bus;
}
-/* Get a block device. This should only be used for single-drive devices
- (e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the
- appropriate bus. */
-DriveInfo *drive_get_next(BlockInterfaceType type)
-{
- static int next_block_unit[IF_COUNT];
-
- return drive_get(type, 0, next_block_unit[type]++);
-}
-
static void bdrv_format_print(void *opaque, const char *name)
{
qemu_printf(" %s", name);
}
}
+static OnOffAuto account_get_opt(QemuOpts *opts, const char *name)
+{
+ if (!qemu_opt_find(opts, name)) {
+ return ON_OFF_AUTO_AUTO;
+ }
+ if (qemu_opt_get_bool(opts, name, true)) {
+ return ON_OFF_AUTO_ON;
+ }
+ return ON_OFF_AUTO_OFF;
+}
+
/* Takes the ownership of bs_opts */
static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
Error **errp)
const char *buf;
int bdrv_flags = 0;
int on_read_error, on_write_error;
- bool account_invalid, account_failed;
+ OnOffAuto account_invalid, account_failed;
bool writethrough, read_only;
BlockBackend *blk;
BlockDriverState *bs;
/* extract parameters */
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
- account_invalid = qemu_opt_get_bool(opts, "stats-account-invalid", true);
- account_failed = qemu_opt_get_bool(opts, "stats-account-failed", true);
+ account_invalid = account_get_opt(opts, "stats-account-invalid");
+ account_failed = account_get_opt(opts, "stats-account-failed");
writethrough = !qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true);
blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
blk_rs = blk_get_root_state(blk);
- blk_rs->open_flags = bdrv_flags;
- blk_rs->read_only = read_only;
+ blk_rs->open_flags = bdrv_flags | (read_only ? 0 : BDRV_O_RDWR);
blk_rs->detect_zeroes = detect_zeroes;
qobject_unref(bs_opts);
{
int bdrv_flags = 0;
+ GLOBAL_STATE_CODE();
/* bdrv_open() defaults to the values in bdrv_flags (for compatibility
* with other callers) rather than what we want as the real defaults.
* Apply the defaults here instead. */
{
BlockDriverState *bs, *next_bs;
+ GLOBAL_STATE_CODE();
QTAILQ_FOREACH_SAFE(bs, &monitor_bdrv_states, monitor_list, next_bs) {
AioContext *ctx = bdrv_get_aio_context(bs);
/* Iterates over the list of monitor-owned BlockDriverStates */
BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs)
{
+ GLOBAL_STATE_CODE();
return bs ? QTAILQ_NEXT(bs, monitor_list)
: QTAILQ_FIRST(&monitor_bdrv_states);
}
const char *filename;
int i;
+ GLOBAL_STATE_CODE();
+
/* Change legacy command line options into QMP ones */
static const struct {
const char *from;
*
* Only prepare() may fail. In a single transaction, only one of commit() or
* abort() will be called. clean() will always be called if it is present.
+ *
+ * Always run under BQL.
*/
typedef struct BlkActionOps {
size_t instance_size;
BlockDriverState *bs;
QEMUSnapshotInfo old_sn, *sn;
bool ret;
- qemu_timeval tv;
+ int64_t rt;
BlockdevSnapshotInternal *internal;
InternalSnapshotState *state;
AioContext *aio_context;
/* 3. take the snapshot */
sn = &state->sn;
pstrcpy(sn->name, sizeof(sn->name), name);
- qemu_gettimeofday(&tv);
- sn->date_sec = tv.tv_sec;
- sn->date_nsec = tv.tv_usec * 1000;
+ rt = g_get_real_time();
+ sn->date_sec = rt / G_USEC_PER_SEC;
+ sn->date_nsec = (rt % G_USEC_PER_SEC) * 1000;
sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
if (replay_mode != REPLAY_MODE_NONE) {
sn->icount = replay_get_current_icount();
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
+ state->bs = bs;
/* Paired with .clean() */
bdrv_drained_begin(bs);
}
}
- state->bs = bs;
-
state->job = do_backup_common(qapi_DriveBackup_base(backup),
bs, target_bs, aio_context,
common->block_job_txn, errp);
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
if (state->job) {
- AioContext *aio_context;
-
- aio_context = bdrv_get_aio_context(state->bs);
- aio_context_acquire(aio_context);
-
- job_cancel_sync(&state->job->job);
-
- aio_context_release(aio_context);
+ job_cancel_sync(&state->job->job, true);
}
}
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
if (state->job) {
- AioContext *aio_context;
-
- aio_context = bdrv_get_aio_context(state->bs);
- aio_context_acquire(aio_context);
-
- job_cancel_sync(&state->job->job);
-
- aio_context_release(aio_context);
+ job_cancel_sync(&state->job->job, true);
}
}
/*
* 'Atomic' group operations. The operations are performed as a set, and if
* any fail then we roll back all operations in the group.
+ *
+ * Always run under BQL.
*/
void qmp_transaction(TransactionActionList *dev_list,
bool has_props,
BlkActionState *state, *next;
Error *local_err = NULL;
+ GLOBAL_STATE_CODE();
+
QTAILQ_HEAD(, BlkActionState) snap_bdrv_states;
QTAILQ_INIT(&snap_bdrv_states);
aio_context_release(aio_context);
}
-/* Get a block job using its ID and acquire its AioContext */
-static BlockJob *find_block_job(const char *id, AioContext **aio_context,
- Error **errp)
+/*
+ * Get a block job using its ID. Called with job_mutex held.
+ */
+static BlockJob *find_block_job_locked(const char *id, Error **errp)
{
BlockJob *job;
assert(id != NULL);
- *aio_context = NULL;
-
- job = block_job_get(id);
+ job = block_job_get_locked(id);
if (!job) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE,
return NULL;
}
- *aio_context = blk_get_aio_context(job->blk);
- aio_context_acquire(*aio_context);
-
return job;
}
void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp)
{
- AioContext *aio_context;
- BlockJob *job = find_block_job(device, &aio_context, errp);
+ BlockJob *job;
+
+ JOB_LOCK_GUARD();
+ job = find_block_job_locked(device, errp);
if (!job) {
return;
}
- block_job_set_speed(job, speed, errp);
- aio_context_release(aio_context);
+ block_job_set_speed_locked(job, speed, errp);
}
void qmp_block_job_cancel(const char *device,
bool has_force, bool force, Error **errp)
{
- AioContext *aio_context;
- BlockJob *job = find_block_job(device, &aio_context, errp);
+ BlockJob *job;
+
+ JOB_LOCK_GUARD();
+ job = find_block_job_locked(device, errp);
if (!job) {
return;
force = false;
}
- if (job_user_paused(&job->job) && !force) {
+ if (job_user_paused_locked(&job->job) && !force) {
error_setg(errp, "The block job for device '%s' is currently paused",
device);
- goto out;
+ return;
}
trace_qmp_block_job_cancel(job);
- job_user_cancel(&job->job, force, errp);
-out:
- aio_context_release(aio_context);
+ job_user_cancel_locked(&job->job, force, errp);
}
void qmp_block_job_pause(const char *device, Error **errp)
{
- AioContext *aio_context;
- BlockJob *job = find_block_job(device, &aio_context, errp);
+ BlockJob *job;
+
+ JOB_LOCK_GUARD();
+ job = find_block_job_locked(device, errp);
if (!job) {
return;
}
trace_qmp_block_job_pause(job);
- job_user_pause(&job->job, errp);
- aio_context_release(aio_context);
+ job_user_pause_locked(&job->job, errp);
}
void qmp_block_job_resume(const char *device, Error **errp)
{
- AioContext *aio_context;
- BlockJob *job = find_block_job(device, &aio_context, errp);
+ BlockJob *job;
+
+ JOB_LOCK_GUARD();
+ job = find_block_job_locked(device, errp);
if (!job) {
return;
}
trace_qmp_block_job_resume(job);
- job_user_resume(&job->job, errp);
- aio_context_release(aio_context);
+ job_user_resume_locked(&job->job, errp);
}
void qmp_block_job_complete(const char *device, Error **errp)
{
- AioContext *aio_context;
- BlockJob *job = find_block_job(device, &aio_context, errp);
+ BlockJob *job;
+
+ JOB_LOCK_GUARD();
+ job = find_block_job_locked(device, errp);
if (!job) {
return;
}
trace_qmp_block_job_complete(job);
- job_complete(&job->job, errp);
- aio_context_release(aio_context);
+ job_complete_locked(&job->job, errp);
}
void qmp_block_job_finalize(const char *id, Error **errp)
{
- AioContext *aio_context;
- BlockJob *job = find_block_job(id, &aio_context, errp);
+ BlockJob *job;
+
+ JOB_LOCK_GUARD();
+ job = find_block_job_locked(id, errp);
if (!job) {
return;
}
trace_qmp_block_job_finalize(job);
- job_ref(&job->job);
- job_finalize(&job->job, errp);
+ job_ref_locked(&job->job);
+ job_finalize_locked(&job->job, errp);
- /*
- * Job's context might have changed via job_finalize (and job_txn_apply
- * automatically acquires the new one), so make sure we release the correct
- * one.
- */
- aio_context = blk_get_aio_context(job->blk);
- job_unref(&job->job);
- aio_context_release(aio_context);
+ job_unref_locked(&job->job);
}
void qmp_block_job_dismiss(const char *id, Error **errp)
{
- AioContext *aio_context;
- BlockJob *bjob = find_block_job(id, &aio_context, errp);
+ BlockJob *bjob;
Job *job;
+ JOB_LOCK_GUARD();
+ bjob = find_block_job_locked(id, errp);
+
if (!bjob) {
return;
}
trace_qmp_block_job_dismiss(bjob);
job = &bjob->job;
- job_dismiss(&job, errp);
- aio_context_release(aio_context);
+ job_dismiss_locked(&job, errp);
}
void qmp_change_backing_file(const char *device,
visit_free(v);
}
-void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
+void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
{
- BlockDriverState *bs;
- AioContext *ctx;
- QObject *obj;
- Visitor *v = qobject_output_visitor_new(&obj);
- BlockReopenQueue *queue;
- QDict *qdict;
+ BlockReopenQueue *queue = NULL;
+ GSList *drained = NULL;
+ GSList *p;
- /* Check for the selected node name */
- if (!options->has_node_name) {
- error_setg(errp, "node-name not specified");
- goto fail;
- }
+ /* Add each one of the BDS that we want to reopen to the queue */
+ for (; reopen_list != NULL; reopen_list = reopen_list->next) {
+ BlockdevOptions *options = reopen_list->value;
+ BlockDriverState *bs;
+ AioContext *ctx;
+ QObject *obj;
+ Visitor *v;
+ QDict *qdict;
- bs = bdrv_find_node(options->node_name);
- if (!bs) {
- error_setg(errp, "Failed to find node with node-name='%s'",
- options->node_name);
- goto fail;
- }
+ /* Check for the selected node name */
+ if (!options->has_node_name) {
+ error_setg(errp, "node-name not specified");
+ goto fail;
+ }
- /* Put all options in a QDict and flatten it */
- visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
- visit_complete(v, &obj);
- qdict = qobject_to(QDict, obj);
+ bs = bdrv_find_node(options->node_name);
+ if (!bs) {
+ error_setg(errp, "Failed to find node with node-name='%s'",
+ options->node_name);
+ goto fail;
+ }
- qdict_flatten(qdict);
+ /* Put all options in a QDict and flatten it */
+ v = qobject_output_visitor_new(&obj);
+ visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
+ visit_complete(v, &obj);
+ visit_free(v);
+
+ qdict = qobject_to(QDict, obj);
+
+ qdict_flatten(qdict);
+
+ ctx = bdrv_get_aio_context(bs);
+ aio_context_acquire(ctx);
+
+ bdrv_subtree_drained_begin(bs);
+ queue = bdrv_reopen_queue(queue, bs, qdict, false);
+ drained = g_slist_prepend(drained, bs);
+
+ aio_context_release(ctx);
+ }
/* 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);
+ queue = NULL;
fail:
- visit_free(v);
+ bdrv_reopen_queue_free(queue);
+ for (p = drained; p; p = p->next) {
+ BlockDriverState *bs = p->data;
+ AioContext *ctx = bdrv_get_aio_context(bs);
+
+ aio_context_acquire(ctx);
+ bdrv_subtree_drained_end(bs);
+ aio_context_release(ctx);
+ }
+ g_slist_free(drained);
}
void qmp_blockdev_del(const char *node_name, Error **errp)
AioContext *aio_context;
BlockDriverState *bs;
+ GLOBAL_STATE_CODE();
+
bs = bdrv_find_node(node_name);
if (!bs) {
error_setg(errp, "Failed to find node with node-name='%s'", node_name);
BlockJobInfoList *head = NULL, **tail = &head;
BlockJob *job;
- for (job = block_job_next(NULL); job; job = block_job_next(job)) {
+ JOB_LOCK_GUARD();
+
+ for (job = block_job_next_locked(NULL); job;
+ job = block_job_next_locked(job)) {
BlockJobInfo *value;
- AioContext *aio_context;
if (block_job_is_internal(job)) {
continue;
}
- aio_context = blk_get_aio_context(job->blk);
- aio_context_acquire(aio_context);
- value = block_job_query(job, errp);
- aio_context_release(aio_context);
+ value = block_job_query_locked(job, errp);
if (!value) {
qapi_free_BlockJobInfoList(head);
return NULL;