diff options
Diffstat (limited to 'nbd/server.c')
-rw-r--r-- | nbd/server.c | 156 |
1 files changed, 71 insertions, 85 deletions
diff --git a/nbd/server.c b/nbd/server.c index 7af0ddffb2..cb0d5634fa 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -77,8 +77,8 @@ struct NBDExport { BlockBackend *blk; char *name; char *description; - off_t dev_offset; - off_t size; + uint64_t dev_offset; + uint64_t size; uint16_t nbdflags; QTAILQ_HEAD(, NBDClient) clients; QTAILQ_ENTRY(NBDExport) next; @@ -1455,10 +1455,11 @@ static void nbd_eject_notifier(Notifier *n, void *data) nbd_export_close(exp); } -NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, - uint16_t nbdflags, void (*close)(NBDExport *), - bool writethrough, BlockBackend *on_eject_blk, - Error **errp) +NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset, + uint64_t size, const char *name, const char *desc, + const char *bitmap, uint16_t nbdflags, + void (*close)(NBDExport *), bool writethrough, + BlockBackend *on_eject_blk, Error **errp) { AioContext *ctx; BlockBackend *blk; @@ -1471,6 +1472,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, * that BDRV_O_INACTIVE is cleared and the image is ready for write * access since the export could be available before migration handover. */ + assert(name); ctx = bdrv_get_aio_context(bs); aio_context_acquire(ctx); bdrv_invalidate_cache(bs, NULL); @@ -1493,15 +1495,50 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, exp->refcount = 1; QTAILQ_INIT(&exp->clients); exp->blk = blk; + assert(dev_offset <= INT64_MAX); exp->dev_offset = dev_offset; + exp->name = g_strdup(name); + exp->description = g_strdup(desc); exp->nbdflags = nbdflags; - exp->size = size < 0 ? blk_getlength(blk) : size; - if (exp->size < 0) { - error_setg_errno(errp, -exp->size, - "Failed to determine the NBD export's length"); - goto fail; + assert(size <= INT64_MAX - dev_offset); + exp->size = QEMU_ALIGN_DOWN(size, BDRV_SECTOR_SIZE); + + if (bitmap) { + BdrvDirtyBitmap *bm = NULL; + BlockDriverState *bs = blk_bs(blk); + + while (true) { + bm = bdrv_find_dirty_bitmap(bs, bitmap); + if (bm != NULL || bs->backing == NULL) { + break; + } + + bs = bs->backing->bs; + } + + if (bm == NULL) { + error_setg(errp, "Bitmap '%s' is not found", bitmap); + goto fail; + } + + if ((nbdflags & NBD_FLAG_READ_ONLY) && bdrv_is_writable(bs) && + bdrv_dirty_bitmap_enabled(bm)) { + error_setg(errp, + "Enabled bitmap '%s' incompatible with readonly export", + bitmap); + goto fail; + } + + if (bdrv_dirty_bitmap_user_locked(bm)) { + error_setg(errp, "Bitmap '%s' is in use", bitmap); + goto fail; + } + + bdrv_dirty_bitmap_set_qmp_locked(bm, true); + exp->export_bitmap = bm; + exp->export_bitmap_context = g_strdup_printf("qemu:dirty-bitmap:%s", + bitmap); } - exp->size -= exp->size % BDRV_SECTOR_SIZE; exp->close = close; exp->ctx = blk_get_aio_context(blk); @@ -1513,10 +1550,14 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, exp->eject_notifier.notify = nbd_eject_notifier; blk_add_remove_bs_notifier(on_eject_blk, &exp->eject_notifier); } + QTAILQ_INSERT_TAIL(&exports, exp, next); + nbd_export_get(exp); return exp; fail: blk_unref(blk); + g_free(exp->name); + g_free(exp->description); g_free(exp); return NULL; } @@ -1533,43 +1574,29 @@ NBDExport *nbd_export_find(const char *name) return NULL; } -void nbd_export_set_name(NBDExport *exp, const char *name) -{ - if (exp->name == name) { - return; - } - - nbd_export_get(exp); - if (exp->name != NULL) { - g_free(exp->name); - exp->name = NULL; - QTAILQ_REMOVE(&exports, exp, next); - nbd_export_put(exp); - } - if (name != NULL) { - nbd_export_get(exp); - exp->name = g_strdup(name); - QTAILQ_INSERT_TAIL(&exports, exp, next); - } - nbd_export_put(exp); -} - -void nbd_export_set_description(NBDExport *exp, const char *description) -{ - g_free(exp->description); - exp->description = g_strdup(description); -} - void nbd_export_close(NBDExport *exp) { NBDClient *client, *next; nbd_export_get(exp); + /* + * TODO: Should we expand QMP NbdServerRemoveNode enum to allow a + * close mode that stops advertising the export to new clients but + * still permits existing clients to run to completion? Because of + * that possibility, nbd_export_close() can be called more than + * once on an export. + */ QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) { client_close(client, true); } - nbd_export_set_name(exp, NULL); - nbd_export_set_description(exp, NULL); + if (exp->name) { + nbd_export_put(exp); + g_free(exp->name); + exp->name = NULL; + QTAILQ_REMOVE(&exports, exp, next); + } + g_free(exp->description); + exp->description = NULL; nbd_export_put(exp); } @@ -1983,7 +2010,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, bool next_dirty = !dirty; if (dirty) { - end = bdrv_dirty_bitmap_next_zero(bitmap, begin); + end = bdrv_dirty_bitmap_next_zero(bitmap, begin, UINT64_MAX); } else { bdrv_set_dirty_iter(it, begin); end = bdrv_dirty_iter_next(it); @@ -2103,10 +2130,10 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, return -EROFS; } if (request->from > client->exp->size || - request->from + request->len > client->exp->size) { + request->len > client->exp->size - request->from) { error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu32 ", Size: %" PRIu64, request->from, request->len, - (uint64_t)client->exp->size); + client->exp->size); return (request->type == NBD_CMD_WRITE || request->type == NBD_CMD_WRITE_ZEROES) ? -ENOSPC : -EINVAL; } @@ -2430,44 +2457,3 @@ void nbd_client_new(QIOChannelSocket *sioc, co = qemu_coroutine_create(nbd_co_client_start, client); qemu_coroutine_enter(co); } - -void nbd_export_bitmap(NBDExport *exp, const char *bitmap, - const char *bitmap_export_name, Error **errp) -{ - BdrvDirtyBitmap *bm = NULL; - BlockDriverState *bs = blk_bs(exp->blk); - - if (exp->export_bitmap) { - error_setg(errp, "Export bitmap is already set"); - return; - } - - while (true) { - bm = bdrv_find_dirty_bitmap(bs, bitmap); - if (bm != NULL || bs->backing == NULL) { - break; - } - - bs = bs->backing->bs; - } - - if (bm == NULL) { - error_setg(errp, "Bitmap '%s' is not found", bitmap); - return; - } - - if (bdrv_dirty_bitmap_enabled(bm)) { - error_setg(errp, "Bitmap '%s' is enabled", bitmap); - return; - } - - if (bdrv_dirty_bitmap_user_locked(bm)) { - error_setg(errp, "Bitmap '%s' is in use", bitmap); - return; - } - - bdrv_dirty_bitmap_set_qmp_locked(bm, true); - exp->export_bitmap = bm; - exp->export_bitmap_context = - g_strdup_printf("qemu:dirty-bitmap:%s", bitmap_export_name); -} |