#include "qemu/timer.h"
#include "qemu/cutils.h"
#include "qemu/id.h"
+#include "qemu/range.h"
+#include "qemu/rcu.h"
#include "block/coroutines.h"
#ifdef CONFIG_BSD
BdrvChildRole child_role,
Error **errp);
+static bool bdrv_recurse_has_child(BlockDriverState *bs,
+ BlockDriverState *child);
+
static void bdrv_replace_child_noperm(BdrvChild *child,
BlockDriverState *new_bs);
static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue,
- Transaction *set_backings_tran, Error **errp);
+ Transaction *change_child_tran, Error **errp);
static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
qemu_co_queue_init(&bs->flush_queue);
+ qemu_co_mutex_init(&bs->bsc_modify_lock);
+ bs->block_status_cache = g_new0(BdrvBlockStatusCache, 1);
+
for (i = 0; i < bdrv_drain_all_count; i++) {
bdrv_drained_begin(bs);
}
return ret;
}
-BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
- int flags, Error **errp)
+/*
+ * Create and open a block node.
+ *
+ * @options is a QDict of options to pass to the block drivers, or NULL for an
+ * empty set of options. The reference to the QDict belongs to the block layer
+ * after the call (even on failure), so if the caller intends to reuse the
+ * dictionary, it needs to use qobject_ref() before calling bdrv_open.
+ */
+BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv,
+ const char *node_name,
+ QDict *options, int flags,
+ Error **errp)
{
BlockDriverState *bs;
int ret;
bs = bdrv_new();
bs->open_flags = flags;
- bs->explicit_options = qdict_new();
- bs->options = qdict_new();
+ bs->options = options ?: qdict_new();
+ bs->explicit_options = qdict_clone_shallow(bs->options);
bs->opaque = NULL;
update_options_from_flags(bs->options, flags);
return bs;
}
+/* Create and open a block node. */
+BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
+ int flags, Error **errp)
+{
+ return bdrv_new_open_driver_opts(drv, node_name, NULL, flags, errp);
+}
+
QemuOptsList bdrv_runtime_opts = {
.name = "bdrv_common",
.head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head),
int drain_saldo;
assert(!child->frozen);
+ assert(old_bs != new_bs);
if (old_bs && new_bs) {
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
assert(parent_bs->drv);
+ if (bdrv_recurse_has_child(child_bs, parent_bs)) {
+ error_setg(errp, "Making '%s' a %s child of '%s' would create a cycle",
+ child_bs->node_name, child_name, parent_bs->node_name);
+ return -EINVAL;
+ }
+
bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
perm, shared_perm, &perm, &shared_perm);
return -EPERM;
}
+ if (is_backing && !parent_bs->drv->is_filter &&
+ !parent_bs->drv->supports_backing)
+ {
+ error_setg(errp, "Driver '%s' of node '%s' does not support backing "
+ "files", parent_bs->drv->format_name, parent_bs->node_name);
+ return -EINVAL;
+ }
+
if (parent_bs->drv->is_filter) {
role = BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY;
} else if (is_backing) {
NULL, 0, keep_old_opts);
}
+void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue)
+{
+ if (bs_queue) {
+ BlockReopenQueueEntry *bs_entry, *next;
+ QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
+ qobject_unref(bs_entry->state.explicit_options);
+ qobject_unref(bs_entry->state.options);
+ g_free(bs_entry);
+ }
+ g_free(bs_queue);
+ }
+}
+
/*
* Reopen multiple BlockDriverStates atomically & transactionally.
*
*
* All affected nodes must be drained between bdrv_reopen_queue() and
* bdrv_reopen_multiple().
+ *
+ * To be called from the main thread, with all other AioContexts unlocked.
*/
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
{
int ret = -1;
BlockReopenQueueEntry *bs_entry, *next;
+ AioContext *ctx;
Transaction *tran = tran_new();
g_autoptr(GHashTable) found = NULL;
g_autoptr(GSList) refresh_list = NULL;
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
assert(bs_queue != NULL);
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
+ ctx = bdrv_get_aio_context(bs_entry->state.bs);
+ aio_context_acquire(ctx);
ret = bdrv_flush(bs_entry->state.bs);
+ aio_context_release(ctx);
if (ret < 0) {
error_setg_errno(errp, -ret, "Error flushing drive");
goto abort;
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
assert(bs_entry->state.bs->quiesce_counter > 0);
+ ctx = bdrv_get_aio_context(bs_entry->state.bs);
+ aio_context_acquire(ctx);
ret = bdrv_reopen_prepare(&bs_entry->state, bs_queue, tran, errp);
+ aio_context_release(ctx);
if (ret < 0) {
goto abort;
}
refresh_list = bdrv_topological_dfs(refresh_list, found,
state->old_backing_bs);
}
+ if (state->old_file_bs) {
+ refresh_list = bdrv_topological_dfs(refresh_list, found,
+ state->old_file_bs);
+ }
}
/*
* to first element.
*/
QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
+ ctx = bdrv_get_aio_context(bs_entry->state.bs);
+ aio_context_acquire(ctx);
bdrv_reopen_commit(&bs_entry->state);
+ aio_context_release(ctx);
}
tran_commit(tran);
BlockDriverState *bs = bs_entry->state.bs;
if (bs->drv->bdrv_reopen_commit_post) {
+ ctx = bdrv_get_aio_context(bs);
+ aio_context_acquire(ctx);
bs->drv->bdrv_reopen_commit_post(&bs_entry->state);
+ aio_context_release(ctx);
}
}
tran_abort(tran);
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
if (bs_entry->prepared) {
+ ctx = bdrv_get_aio_context(bs_entry->state.bs);
+ aio_context_acquire(ctx);
bdrv_reopen_abort(&bs_entry->state);
+ aio_context_release(ctx);
}
- qobject_unref(bs_entry->state.explicit_options);
- qobject_unref(bs_entry->state.options);
}
cleanup:
- QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
- g_free(bs_entry);
- }
- g_free(bs_queue);
+ bdrv_reopen_queue_free(bs_queue);
return ret;
}
-int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
- Error **errp)
+int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts,
+ Error **errp)
{
- int ret;
+ AioContext *ctx = bdrv_get_aio_context(bs);
BlockReopenQueue *queue;
- QDict *opts = qdict_new();
-
- qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only);
+ int ret;
bdrv_subtree_drained_begin(bs);
- queue = bdrv_reopen_queue(NULL, bs, opts, true);
+ if (ctx != qemu_get_aio_context()) {
+ aio_context_release(ctx);
+ }
+
+ queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts);
ret = bdrv_reopen_multiple(queue, errp);
+
+ if (ctx != qemu_get_aio_context()) {
+ aio_context_acquire(ctx);
+ }
bdrv_subtree_drained_end(bs);
return ret;
}
-static bool bdrv_reopen_can_attach(BlockDriverState *parent,
- BdrvChild *child,
- BlockDriverState *new_child,
- Error **errp)
+int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
+ Error **errp)
{
- AioContext *parent_ctx = bdrv_get_aio_context(parent);
- AioContext *child_ctx = bdrv_get_aio_context(new_child);
- GSList *ignore;
- bool ret;
+ QDict *opts = qdict_new();
- ignore = g_slist_prepend(NULL, child);
- ret = bdrv_can_set_aio_context(new_child, parent_ctx, &ignore, NULL);
- g_slist_free(ignore);
- if (ret) {
- return ret;
- }
+ qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only);
- ignore = g_slist_prepend(NULL, child);
- ret = bdrv_can_set_aio_context(parent, child_ctx, &ignore, errp);
- g_slist_free(ignore);
- return ret;
+ return bdrv_reopen(bs, opts, true, errp);
}
/*
*
* Return 0 on success, otherwise return < 0 and set @errp.
*/
-static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
- Transaction *set_backings_tran,
- Error **errp)
+static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state,
+ bool is_backing, Transaction *tran,
+ Error **errp)
{
BlockDriverState *bs = reopen_state->bs;
- BlockDriverState *overlay_bs, *below_bs, *new_backing_bs;
+ BlockDriverState *new_child_bs;
+ BlockDriverState *old_child_bs = is_backing ? child_bs(bs->backing) :
+ child_bs(bs->file);
+ const char *child_name = is_backing ? "backing" : "file";
QObject *value;
const char *str;
- value = qdict_get(reopen_state->options, "backing");
+ value = qdict_get(reopen_state->options, child_name);
if (value == NULL) {
return 0;
}
switch (qobject_type(value)) {
case QTYPE_QNULL:
- new_backing_bs = NULL;
+ assert(is_backing); /* The 'file' option does not allow a null value */
+ new_child_bs = NULL;
break;
case QTYPE_QSTRING:
str = qstring_get_str(qobject_to(QString, value));
- new_backing_bs = bdrv_lookup_bs(NULL, str, errp);
- if (new_backing_bs == NULL) {
+ new_child_bs = bdrv_lookup_bs(NULL, str, errp);
+ if (new_child_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);
+ } else if (bdrv_recurse_has_child(new_child_bs, bs)) {
+ error_setg(errp, "Making '%s' a %s child of '%s' would create a "
+ "cycle", str, child_name, bs->node_name);
return -EINVAL;
}
break;
default:
- /* 'backing' does not allow any other data type */
+ /*
+ * The options QDict has been flattened, so 'backing' and 'file'
+ * do not allow any other data type here.
+ */
g_assert_not_reached();
}
- /*
- * Check AioContext compatibility so that the bdrv_set_backing_hd() call in
- * bdrv_reopen_commit() won't fail.
- */
- if (new_backing_bs) {
- if (!bdrv_reopen_can_attach(bs, bs->backing, new_backing_bs, errp)) {
- return -EINVAL;
- }
+ if (old_child_bs == new_child_bs) {
+ return 0;
}
- /*
- * Ensure that @bs can really handle backing files, because we are
- * about to give it one (or swap the existing one)
- */
- if (bs->drv->is_filter) {
- /* Filters always have a file or a backing child */
- if (!bs->backing) {
- error_setg(errp, "'%s' is a %s filter node that does not support a "
- "backing child", bs->node_name, bs->drv->format_name);
- return -EINVAL;
+ if (old_child_bs) {
+ if (bdrv_skip_implicit_filters(old_child_bs) == new_child_bs) {
+ return 0;
}
- } else if (!bs->drv->supports_backing) {
- error_setg(errp, "Driver '%s' of node '%s' does not support backing "
- "files", bs->drv->format_name, bs->node_name);
- 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).
- * We cannot use any of the bdrv_skip_*() functions here because
- * those return the first explicit node, while we are looking for
- * its overlay here.
- */
- overlay_bs = bs;
- for (below_bs = bdrv_filter_or_cow_bs(overlay_bs);
- below_bs && below_bs->implicit;
- below_bs = bdrv_filter_or_cow_bs(overlay_bs))
- {
- overlay_bs = below_bs;
- }
- /* If we want to replace the backing file we need some extra checks */
- if (new_backing_bs != bdrv_filter_or_cow_bs(overlay_bs)) {
- int ret;
-
- /* 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);
+ if (old_child_bs->implicit) {
+ error_setg(errp, "Cannot replace implicit %s child of %s",
+ child_name, bs->node_name);
return -EPERM;
}
+ }
+
+ if (bs->drv->is_filter && !old_child_bs) {
/*
- * Check if the backing link that we want to replace is frozen.
- * Note that
- * bdrv_filter_or_cow_child(overlay_bs) == overlay_bs->backing,
- * because we know that overlay_bs == bs, and that @bs
- * either is a filter that uses ->backing or a COW format BDS
- * with bs->drv->supports_backing == true.
+ * Filters always have a file or a backing child, so we are trying to
+ * change wrong child
*/
- if (bdrv_is_backing_chain_frozen(overlay_bs,
- child_bs(overlay_bs->backing), errp))
- {
- return -EPERM;
- }
- reopen_state->replace_backing_bs = true;
- reopen_state->old_backing_bs = bs->backing ? bs->backing->bs : NULL;
- ret = bdrv_set_backing_noperm(bs, new_backing_bs, set_backings_tran,
- errp);
- if (ret < 0) {
- return ret;
- }
+ error_setg(errp, "'%s' is a %s filter node that does not support a "
+ "%s child", bs->node_name, bs->drv->format_name, child_name);
+ return -EINVAL;
}
- return 0;
+ if (is_backing) {
+ reopen_state->old_backing_bs = old_child_bs;
+ } else {
+ reopen_state->old_file_bs = old_child_bs;
+ }
+
+ return bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing,
+ tran, errp);
}
/*
*/
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue,
- Transaction *set_backings_tran, Error **errp)
+ Transaction *change_child_tran, Error **errp)
{
int ret = -1;
int old_flags;
* 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, set_backings_tran, errp);
+ ret = bdrv_reopen_parse_file_or_backing(reopen_state, true,
+ change_child_tran, errp);
if (ret < 0) {
goto error;
}
qdict_del(reopen_state->options, "backing");
+ /* Allow changing the 'file' option. In this case NULL is not allowed */
+ ret = bdrv_reopen_parse_file_or_backing(reopen_state, false,
+ change_child_tran, errp);
+ if (ret < 0) {
+ goto error;
+ }
+ qdict_del(reopen_state->options, "file");
+
/* 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) */
/* set BDS specific flags now */
qobject_unref(bs->explicit_options);
qobject_unref(bs->options);
+ qobject_ref(reopen_state->explicit_options);
+ qobject_ref(reopen_state->options);
bs->explicit_options = reopen_state->explicit_options;
bs->options = reopen_state->options;
bs->open_flags = reopen_state->flags;
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) {
qdict_del(bs->explicit_options, child->name);
qdict_del(bs->options, child->name);
}
+ /* backing is probably removed, so it's not handled by previous loop */
+ qdict_del(bs->explicit_options, "backing");
+ qdict_del(bs->options, "backing");
+
bdrv_refresh_limits(bs, NULL, NULL);
}
bs->explicit_options = NULL;
qobject_unref(bs->full_open_options);
bs->full_open_options = NULL;
+ g_free(bs->block_status_cache);
+ bs->block_status_cache = NULL;
bdrv_release_named_dirty_bitmaps(bs);
assert(QLIST_EMPTY(&bs->dirty_bitmaps));
return ret;
}
+/* Not for empty child */
+int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs,
+ Error **errp)
+{
+ int ret;
+ Transaction *tran = tran_new();
+ g_autoptr(GHashTable) found = NULL;
+ g_autoptr(GSList) refresh_list = NULL;
+ BlockDriverState *old_bs = child->bs;
+
+ bdrv_ref(old_bs);
+ bdrv_drained_begin(old_bs);
+ bdrv_drained_begin(new_bs);
+
+ bdrv_replace_child_tran(child, new_bs, tran);
+
+ found = g_hash_table_new(NULL, NULL);
+ refresh_list = bdrv_topological_dfs(refresh_list, found, old_bs);
+ refresh_list = bdrv_topological_dfs(refresh_list, found, new_bs);
+
+ ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp);
+
+ tran_finalize(tran, ret);
+
+ bdrv_drained_end(old_bs);
+ bdrv_drained_end(new_bs);
+ bdrv_unref(old_bs);
+
+ return ret;
+}
+
static void bdrv_delete(BlockDriverState *bs)
{
assert(bdrv_op_blocker_is_empty(bs));
g_free(bs);
}
-BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options,
+
+/*
+ * Replace @bs by newly created block node.
+ *
+ * @options is a QDict of options to pass to the block drivers, or NULL for an
+ * empty set of options. The reference to the QDict belongs to the block layer
+ * after the call (even on failure), so if the caller intends to reuse the
+ * dictionary, it needs to use qobject_ref() before calling bdrv_open.
+ */
+BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options,
int flags, Error **errp)
{
- BlockDriverState *new_node_bs;
- Error *local_err = NULL;
+ ERRP_GUARD();
+ int ret;
+ BlockDriverState *new_node_bs = NULL;
+ const char *drvname, *node_name;
+ BlockDriver *drv;
- new_node_bs = bdrv_open(NULL, NULL, node_options, flags, errp);
- if (new_node_bs == NULL) {
+ drvname = qdict_get_try_str(options, "driver");
+ if (!drvname) {
+ error_setg(errp, "driver is not specified");
+ goto fail;
+ }
+
+ drv = bdrv_find_format(drvname);
+ if (!drv) {
+ error_setg(errp, "Unknown driver: '%s'", drvname);
+ goto fail;
+ }
+
+ node_name = qdict_get_try_str(options, "node-name");
+
+ new_node_bs = bdrv_new_open_driver_opts(drv, node_name, options, flags,
+ errp);
+ options = NULL; /* bdrv_new_open_driver() eats options */
+ if (!new_node_bs) {
error_prepend(errp, "Could not create node: ");
- return NULL;
+ goto fail;
}
bdrv_drained_begin(bs);
- bdrv_replace_node(bs, new_node_bs, &local_err);
+ ret = bdrv_replace_node(bs, new_node_bs, errp);
bdrv_drained_end(bs);
- if (local_err) {
- bdrv_unref(new_node_bs);
- error_propagate(errp, local_err);
- return NULL;
+ if (ret < 0) {
+ error_prepend(errp, "Could not replace node: ");
+ goto fail;
}
return new_node_bs;
+
+fail:
+ qobject_unref(options);
+ bdrv_unref(new_node_bs);
+ return NULL;
}
/*
* -ENOTSUP - format driver doesn't support changing the backing file
*/
int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file,
- const char *backing_fmt, bool warn)
+ const char *backing_fmt, bool require)
{
BlockDriver *drv = bs->drv;
int ret;
return -EINVAL;
}
- if (warn && backing_file && !backing_fmt) {
- warn_report("Deprecated use of backing file without explicit "
- "backing format, use of this image requires "
- "potentially unsafe format probing");
+ if (require && backing_file && !backing_fmt) {
+ return -EINVAL;
}
if (drv->bdrv_change_backing_file != NULL) {
void bdrv_init(void)
{
+#ifdef CONFIG_BDRV_WHITELIST_TOOLS
+ use_bdrv_whitelist = 1;
+#endif
module_call_init(MODULE_INIT_BLOCK);
}
{
BdrvChild *child, *parent;
int ret;
+ uint64_t cumulative_perms, cumulative_shared_perms;
if (!bs->drv) {
return -ENOMEDIUM;
}
}
+ bdrv_get_cumulative_perm(bs, &cumulative_perms,
+ &cumulative_shared_perms);
+ if (cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) {
+ /* Our inactive parents still need write access. Inactivation failed. */
+ return -EPERM;
+ }
+
bs->open_flags |= BDRV_O_INACTIVE;
/*
goto out;
} else {
if (!backing_fmt) {
- warn_report("Deprecated use of backing file without explicit "
- "backing format (detected format of %s)",
- bs->drv->format_name);
- if (bs->drv != &bdrv_raw) {
- /*
- * A probe of raw deserves the most attention:
- * leaving the backing format out of the image
- * will ensure bs->probed is set (ensuring we
- * don't accidentally commit into the backing
- * file), and allow more spots to warn the users
- * to fix their toolchain when opening this image
- * later. For other images, we can safely record
- * the format that we probed.
- */
- backing_fmt = bs->drv->format_name;
- qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, backing_fmt,
- NULL);
- }
+ error_setg(&local_err,
+ "Backing file specified without backing format");
+ error_append_hint(&local_err, "Detected format of %s.",
+ bs->drv->format_name);
+ goto out;
}
if (size == -1) {
/* Opened BS, have no size */
}
/* (backing_file && !(flags & BDRV_O_NO_BACKING)) */
} else if (backing_file && !backing_fmt) {
- warn_report("Deprecated use of unopened backing file without "
- "explicit backing format, use of this image requires "
- "potentially unsafe format probing");
+ error_setg(&local_err,
+ "Backing file specified without backing format");
+ goto out;
}
if (size == -1) {
{
return bdrv_skip_filters(bdrv_cow_bs(bdrv_skip_filters(bs)));
}
+
+/**
+ * Check whether [offset, offset + bytes) overlaps with the cached
+ * block-status data region.
+ *
+ * If so, and @pnum is not NULL, set *pnum to `bsc.data_end - offset`,
+ * which is what bdrv_bsc_is_data()'s interface needs.
+ * Otherwise, *pnum is not touched.
+ */
+static bool bdrv_bsc_range_overlaps_locked(BlockDriverState *bs,
+ int64_t offset, int64_t bytes,
+ int64_t *pnum)
+{
+ BdrvBlockStatusCache *bsc = qatomic_rcu_read(&bs->block_status_cache);
+ bool overlaps;
+
+ overlaps =
+ qatomic_read(&bsc->valid) &&
+ ranges_overlap(offset, bytes, bsc->data_start,
+ bsc->data_end - bsc->data_start);
+
+ if (overlaps && pnum) {
+ *pnum = bsc->data_end - offset;
+ }
+
+ return overlaps;
+}
+
+/**
+ * See block_int.h for this function's documentation.
+ */
+bool bdrv_bsc_is_data(BlockDriverState *bs, int64_t offset, int64_t *pnum)
+{
+ RCU_READ_LOCK_GUARD();
+
+ return bdrv_bsc_range_overlaps_locked(bs, offset, 1, pnum);
+}
+
+/**
+ * See block_int.h for this function's documentation.
+ */
+void bdrv_bsc_invalidate_range(BlockDriverState *bs,
+ int64_t offset, int64_t bytes)
+{
+ RCU_READ_LOCK_GUARD();
+
+ if (bdrv_bsc_range_overlaps_locked(bs, offset, bytes, NULL)) {
+ qatomic_set(&bs->block_status_cache->valid, false);
+ }
+}
+
+/**
+ * See block_int.h for this function's documentation.
+ */
+void bdrv_bsc_fill(BlockDriverState *bs, int64_t offset, int64_t bytes)
+{
+ BdrvBlockStatusCache *new_bsc = g_new(BdrvBlockStatusCache, 1);
+ BdrvBlockStatusCache *old_bsc;
+
+ *new_bsc = (BdrvBlockStatusCache) {
+ .valid = true,
+ .data_start = offset,
+ .data_end = offset + bytes,
+ };
+
+ QEMU_LOCK_GUARD(&bs->bsc_modify_lock);
+
+ old_bsc = qatomic_rcu_read(&bs->block_status_cache);
+ qatomic_rcu_set(&bs->block_status_cache, new_bsc);
+ if (old_bsc) {
+ g_free_rcu(old_bsc, rcu);
+ }
+}