123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- /*
- * Common block export infrastructure
- *
- * Copyright (c) 2012, 2020 Red Hat, Inc.
- *
- * Authors:
- * Paolo Bonzini <pbonzini@redhat.com>
- * Kevin Wolf <kwolf@redhat.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.
- */
- #include "qemu/osdep.h"
- #include "block/block.h"
- #include "system/block-backend.h"
- #include "system/iothread.h"
- #include "block/export.h"
- #include "block/fuse.h"
- #include "block/nbd.h"
- #include "qapi/error.h"
- #include "qapi/qapi-commands-block-export.h"
- #include "qapi/qapi-events-block-export.h"
- #include "qemu/id.h"
- #ifdef CONFIG_VHOST_USER_BLK_SERVER
- #include "vhost-user-blk-server.h"
- #endif
- #ifdef CONFIG_VDUSE_BLK_EXPORT
- #include "vduse-blk.h"
- #endif
- static const BlockExportDriver *blk_exp_drivers[] = {
- &blk_exp_nbd,
- #ifdef CONFIG_VHOST_USER_BLK_SERVER
- &blk_exp_vhost_user_blk,
- #endif
- #ifdef CONFIG_FUSE
- &blk_exp_fuse,
- #endif
- #ifdef CONFIG_VDUSE_BLK_EXPORT
- &blk_exp_vduse_blk,
- #endif
- };
- /* Only accessed from the main thread */
- static QLIST_HEAD(, BlockExport) block_exports =
- QLIST_HEAD_INITIALIZER(block_exports);
- BlockExport *blk_exp_find(const char *id)
- {
- BlockExport *exp;
- QLIST_FOREACH(exp, &block_exports, next) {
- if (strcmp(id, exp->id) == 0) {
- return exp;
- }
- }
- return NULL;
- }
- static const BlockExportDriver *blk_exp_find_driver(BlockExportType type)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(blk_exp_drivers); i++) {
- if (blk_exp_drivers[i]->type == type) {
- return blk_exp_drivers[i];
- }
- }
- return NULL;
- }
- BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
- {
- bool fixed_iothread = export->has_fixed_iothread && export->fixed_iothread;
- const BlockExportDriver *drv;
- BlockExport *exp = NULL;
- BlockDriverState *bs;
- BlockBackend *blk = NULL;
- AioContext *ctx;
- uint64_t perm;
- int ret;
- GLOBAL_STATE_CODE();
- if (!id_wellformed(export->id)) {
- error_setg(errp, "Invalid block export id");
- return NULL;
- }
- if (blk_exp_find(export->id)) {
- error_setg(errp, "Block export id '%s' is already in use", export->id);
- return NULL;
- }
- drv = blk_exp_find_driver(export->type);
- if (!drv) {
- error_setg(errp, "No driver found for the requested export type");
- return NULL;
- }
- bs = bdrv_lookup_bs(NULL, export->node_name, errp);
- if (!bs) {
- return NULL;
- }
- if (!export->has_writable) {
- export->writable = false;
- }
- if (bdrv_is_read_only(bs) && export->writable) {
- error_setg(errp, "Cannot export read-only node as writable");
- return NULL;
- }
- ctx = bdrv_get_aio_context(bs);
- if (export->iothread) {
- IOThread *iothread;
- AioContext *new_ctx;
- Error **set_context_errp;
- iothread = iothread_by_id(export->iothread);
- if (!iothread) {
- error_setg(errp, "iothread \"%s\" not found", export->iothread);
- goto fail;
- }
- new_ctx = iothread_get_aio_context(iothread);
- /* Ignore errors with fixed-iothread=false */
- set_context_errp = fixed_iothread ? errp : NULL;
- ret = bdrv_try_change_aio_context(bs, new_ctx, NULL, set_context_errp);
- if (ret == 0) {
- ctx = new_ctx;
- } else if (fixed_iothread) {
- goto fail;
- }
- }
- /*
- * Block exports are used for non-shared storage migration. Make sure
- * that BDRV_O_INACTIVE is cleared and the image is ready for write
- * access since the export could be available before migration handover.
- * ctx was acquired in the caller.
- */
- bdrv_graph_rdlock_main_loop();
- bdrv_activate(bs, NULL);
- bdrv_graph_rdunlock_main_loop();
- perm = BLK_PERM_CONSISTENT_READ;
- if (export->writable) {
- perm |= BLK_PERM_WRITE;
- }
- blk = blk_new(ctx, perm, BLK_PERM_ALL);
- if (!fixed_iothread) {
- blk_set_allow_aio_context_change(blk, true);
- }
- ret = blk_insert_bs(blk, bs, errp);
- if (ret < 0) {
- goto fail;
- }
- if (!export->has_writethrough) {
- export->writethrough = false;
- }
- blk_set_enable_write_cache(blk, !export->writethrough);
- assert(drv->instance_size >= sizeof(BlockExport));
- exp = g_malloc0(drv->instance_size);
- *exp = (BlockExport) {
- .drv = drv,
- .refcount = 1,
- .user_owned = true,
- .id = g_strdup(export->id),
- .ctx = ctx,
- .blk = blk,
- };
- ret = drv->create(exp, export, errp);
- if (ret < 0) {
- goto fail;
- }
- assert(exp->blk != NULL);
- QLIST_INSERT_HEAD(&block_exports, exp, next);
- return exp;
- fail:
- if (blk) {
- blk_set_dev_ops(blk, NULL, NULL);
- blk_unref(blk);
- }
- if (exp) {
- g_free(exp->id);
- g_free(exp);
- }
- return NULL;
- }
- void blk_exp_ref(BlockExport *exp)
- {
- assert(qatomic_read(&exp->refcount) > 0);
- qatomic_inc(&exp->refcount);
- }
- /* Runs in the main thread */
- static void blk_exp_delete_bh(void *opaque)
- {
- BlockExport *exp = opaque;
- assert(exp->refcount == 0);
- QLIST_REMOVE(exp, next);
- exp->drv->delete(exp);
- blk_set_dev_ops(exp->blk, NULL, NULL);
- blk_unref(exp->blk);
- qapi_event_send_block_export_deleted(exp->id);
- g_free(exp->id);
- g_free(exp);
- }
- void blk_exp_unref(BlockExport *exp)
- {
- assert(qatomic_read(&exp->refcount) > 0);
- if (qatomic_fetch_dec(&exp->refcount) == 1) {
- /* Touch the block_exports list only in the main thread */
- aio_bh_schedule_oneshot(qemu_get_aio_context(), blk_exp_delete_bh,
- exp);
- }
- }
- /*
- * Drops the user reference to the export and requests that all client
- * connections and other internally held references start to shut down. When
- * the function returns, there may still be active references while the export
- * is in the process of shutting down.
- */
- void blk_exp_request_shutdown(BlockExport *exp)
- {
- /*
- * If the user doesn't own the export any more, it is already shutting
- * down. We must not call .request_shutdown and decrease the refcount a
- * second time.
- */
- if (!exp->user_owned) {
- return;
- }
- exp->drv->request_shutdown(exp);
- assert(exp->user_owned);
- exp->user_owned = false;
- blk_exp_unref(exp);
- }
- /*
- * Returns whether a block export of the given type exists.
- * type == BLOCK_EXPORT_TYPE__MAX checks for an export of any type.
- */
- static bool blk_exp_has_type(BlockExportType type)
- {
- BlockExport *exp;
- if (type == BLOCK_EXPORT_TYPE__MAX) {
- return !QLIST_EMPTY(&block_exports);
- }
- QLIST_FOREACH(exp, &block_exports, next) {
- if (exp->drv->type == type) {
- return true;
- }
- }
- return false;
- }
- /* type == BLOCK_EXPORT_TYPE__MAX for all types */
- void blk_exp_close_all_type(BlockExportType type)
- {
- BlockExport *exp, *next;
- assert(in_aio_context_home_thread(qemu_get_aio_context()));
- QLIST_FOREACH_SAFE(exp, &block_exports, next, next) {
- if (type != BLOCK_EXPORT_TYPE__MAX && exp->drv->type != type) {
- continue;
- }
- blk_exp_request_shutdown(exp);
- }
- AIO_WAIT_WHILE_UNLOCKED(NULL, blk_exp_has_type(type));
- }
- void blk_exp_close_all(void)
- {
- blk_exp_close_all_type(BLOCK_EXPORT_TYPE__MAX);
- }
- void qmp_block_export_add(BlockExportOptions *export, Error **errp)
- {
- blk_exp_add(export, errp);
- }
- void qmp_block_export_del(const char *id,
- bool has_mode, BlockExportRemoveMode mode,
- Error **errp)
- {
- ERRP_GUARD();
- BlockExport *exp;
- exp = blk_exp_find(id);
- if (exp == NULL) {
- error_setg(errp, "Export '%s' is not found", id);
- return;
- }
- if (!exp->user_owned) {
- error_setg(errp, "Export '%s' is already shutting down", id);
- return;
- }
- if (!has_mode) {
- mode = BLOCK_EXPORT_REMOVE_MODE_SAFE;
- }
- if (mode == BLOCK_EXPORT_REMOVE_MODE_SAFE &&
- qatomic_read(&exp->refcount) > 1) {
- error_setg(errp, "export '%s' still in use", exp->id);
- error_append_hint(errp, "Use mode='hard' to force client "
- "disconnect\n");
- return;
- }
- blk_exp_request_shutdown(exp);
- }
- BlockExportInfoList *qmp_query_block_exports(Error **errp)
- {
- BlockExportInfoList *head = NULL, **tail = &head;
- BlockExport *exp;
- QLIST_FOREACH(exp, &block_exports, next) {
- BlockExportInfo *info = g_new(BlockExportInfo, 1);
- *info = (BlockExportInfo) {
- .id = g_strdup(exp->id),
- .type = exp->drv->type,
- .node_name = g_strdup(bdrv_get_node_name(blk_bs(exp->blk))),
- .shutting_down = !exp->user_owned,
- };
- QAPI_LIST_APPEND(tail, info);
- }
- return head;
- }
|