*/
#include "qemu/osdep.h"
-#include "qapi-visit.h"
#include "qapi/error.h"
+#include "qapi/qapi-visit-sockets.h"
+#include "qapi/qapi-visit-block-core.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qobject-input-visitor.h"
+#include "qapi/qobject-output-visitor.h"
#include "qemu/uri.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
qemu_co_mutex_unlock(&s->queue_lock);
}
-static SocketAddress *sd_socket_address(const char *path,
- const char *host, const char *port)
-{
- SocketAddress *addr = g_new0(SocketAddress, 1);
-
- if (path) {
- addr->type = SOCKET_ADDRESS_TYPE_UNIX;
- addr->u.q_unix.path = g_strdup(path);
- } else {
- addr->type = SOCKET_ADDRESS_TYPE_INET;
- addr->u.inet.host = g_strdup(host ?: SD_DEFAULT_ADDR);
- addr->u.inet.port = g_strdup(port ?: stringify(SD_DEFAULT_PORT));
- }
-
- return addr;
-}
-
static SocketAddress *sd_server_config(QDict *options, Error **errp)
{
QDict *server = NULL;
cfg->uri = uri = uri_parse(filename);
if (!uri) {
- error_setg(&err, "invalid URI");
+ error_setg(&err, "invalid URI '%s'", filename);
goto out;
}
return ret;
}
+static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
+ Error **errp)
+{
+ BlockDriverState *bs;
+ Visitor *v;
+ QObject *obj = NULL;
+ QDict *qdict;
+ Error *local_err = NULL;
+ int ret;
+
+ v = qobject_output_visitor_new(&obj);
+ visit_type_BlockdevOptionsSheepdog(v, NULL, &location, &local_err);
+ visit_free(v);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ qobject_decref(obj);
+ return -EINVAL;
+ }
+
+ qdict = qobject_to(QDict, obj);
+ qdict_flatten(qdict);
+
+ qdict_put_str(qdict, "driver", "sheepdog");
+
+ bs = bdrv_open(NULL, NULL, qdict, BDRV_O_PROTOCOL | BDRV_O_RDWR, errp);
+ if (bs == NULL) {
+ ret = -EIO;
+ goto fail;
+ }
+
+ ret = sd_prealloc(bs, 0, size, errp);
+fail:
+ bdrv_unref(bs);
+ QDECREF(qdict);
+ return ret;
+}
+
+static int parse_redundancy(BDRVSheepdogState *s, SheepdogRedundancy *opt)
+{
+ struct SheepdogInode *inode = &s->inode;
+
+ switch (opt->type) {
+ case SHEEPDOG_REDUNDANCY_TYPE_FULL:
+ if (opt->u.full.copies > SD_MAX_COPIES || opt->u.full.copies < 1) {
+ return -EINVAL;
+ }
+ inode->copy_policy = 0;
+ inode->nr_copies = opt->u.full.copies;
+ return 0;
+
+ case SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED:
+ {
+ int64_t copy = opt->u.erasure_coded.data_strips;
+ int64_t parity = opt->u.erasure_coded.parity_strips;
+
+ if (copy != 2 && copy != 4 && copy != 8 && copy != 16) {
+ return -EINVAL;
+ }
+
+ if (parity >= SD_EC_MAX_STRIP || parity < 1) {
+ return -EINVAL;
+ }
+
+ /*
+ * 4 bits for parity and 4 bits for data.
+ * We have to compress upper data bits because it can't represent 16
+ */
+ inode->copy_policy = ((copy / 2) << 4) + parity;
+ inode->nr_copies = copy + parity;
+ return 0;
+ }
+
+ default:
+ g_assert_not_reached();
+ }
+
+ return -EINVAL;
+}
+
/*
* Sheepdog support two kinds of redundancy, full replication and erasure
* coding.
* # create a erasure coded vdi with x data strips and y parity strips
* -o redundancy=x:y (x must be one of {2,4,8,16} and 1 <= y < SD_EC_MAX_STRIP)
*/
-static int parse_redundancy(BDRVSheepdogState *s, const char *opt)
+static SheepdogRedundancy *parse_redundancy_str(const char *opt)
{
- struct SheepdogInode *inode = &s->inode;
+ SheepdogRedundancy *redundancy;
const char *n1, *n2;
long copy, parity;
char p[10];
+ int ret;
pstrcpy(p, sizeof(p), opt);
n1 = strtok(p, ":");
n2 = strtok(NULL, ":");
if (!n1) {
- return -EINVAL;
+ return NULL;
}
- copy = strtol(n1, NULL, 10);
- /* FIXME fix error checking by switching to qemu_strtol() */
- if (copy > SD_MAX_COPIES || copy < 1) {
- return -EINVAL;
+ ret = qemu_strtol(n1, NULL, 10, ©);
+ if (ret < 0) {
+ return NULL;
}
+
+ redundancy = g_new0(SheepdogRedundancy, 1);
if (!n2) {
- inode->copy_policy = 0;
- inode->nr_copies = copy;
- return 0;
- }
+ *redundancy = (SheepdogRedundancy) {
+ .type = SHEEPDOG_REDUNDANCY_TYPE_FULL,
+ .u.full.copies = copy,
+ };
+ } else {
+ ret = qemu_strtol(n2, NULL, 10, &parity);
+ if (ret < 0) {
+ return NULL;
+ }
- if (copy != 2 && copy != 4 && copy != 8 && copy != 16) {
- return -EINVAL;
+ *redundancy = (SheepdogRedundancy) {
+ .type = SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED,
+ .u.erasure_coded = {
+ .data_strips = copy,
+ .parity_strips = parity,
+ },
+ };
}
- parity = strtol(n2, NULL, 10);
- /* FIXME fix error checking by switching to qemu_strtol() */
- if (parity >= SD_EC_MAX_STRIP || parity < 1) {
- return -EINVAL;
- }
-
- /*
- * 4 bits for parity and 4 bits for data.
- * We have to compress upper data bits because it can't represent 16
- */
- inode->copy_policy = ((copy / 2) << 4) + parity;
- inode->nr_copies = copy + parity;
-
- return 0;
+ return redundancy;
}
-static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
+static int parse_block_size_shift(BDRVSheepdogState *s,
+ BlockdevCreateOptionsSheepdog *opts)
{
struct SheepdogInode *inode = &s->inode;
uint64_t object_size;
int obj_order;
- object_size = qemu_opt_get_size_del(opt, BLOCK_OPT_OBJECT_SIZE, 0);
- if (object_size) {
+ if (opts->has_object_size) {
+ object_size = opts->object_size;
+
if ((object_size - 1) & object_size) { /* not a power of 2? */
return -EINVAL;
}
return 0;
}
-static int sd_create(const char *filename, QemuOpts *opts,
- Error **errp)
+static int sd_co_create(BlockdevCreateOptions *options, Error **errp)
{
- Error *err = NULL;
+ BlockdevCreateOptionsSheepdog *opts = &options->u.sheepdog;
int ret = 0;
uint32_t vid = 0;
char *backing_file = NULL;
char *buf = NULL;
BDRVSheepdogState *s;
- SheepdogConfig cfg;
uint64_t max_vdi_size;
bool prealloc = false;
+ assert(options->driver == BLOCKDEV_DRIVER_SHEEPDOG);
+
s = g_new0(BDRVSheepdogState, 1);
- if (strstr(filename, "://")) {
- sd_parse_uri(&cfg, filename, &err);
- } else {
- parse_vdiname(&cfg, filename, &err);
- }
- if (err) {
- error_propagate(errp, err);
+ /* Steal SocketAddress from QAPI, set NULL to prevent double free */
+ s->addr = opts->location->server;
+ opts->location->server = NULL;
+
+ if (strlen(opts->location->vdi) >= sizeof(s->name)) {
+ error_setg(errp, "'vdi' string too long");
+ ret = -EINVAL;
goto out;
}
+ pstrcpy(s->name, sizeof(s->name), opts->location->vdi);
- buf = cfg.port ? g_strdup_printf("%d", cfg.port) : NULL;
- s->addr = sd_socket_address(cfg.path, cfg.host, buf);
- g_free(buf);
- strcpy(s->name, cfg.vdi);
- sd_config_done(&cfg);
+ s->inode.vdi_size = opts->size;
+ backing_file = opts->backing_file;
- s->inode.vdi_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
- buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
- if (!buf || !strcmp(buf, "off")) {
+ if (!opts->has_preallocation) {
+ opts->preallocation = PREALLOC_MODE_OFF;
+ }
+ switch (opts->preallocation) {
+ case PREALLOC_MODE_OFF:
prealloc = false;
- } else if (!strcmp(buf, "full")) {
+ break;
+ case PREALLOC_MODE_FULL:
prealloc = true;
- } else {
- error_setg(errp, "Invalid preallocation mode: '%s'", buf);
+ break;
+ default:
+ error_setg(errp, "Preallocation mode not supported for Sheepdog");
ret = -EINVAL;
goto out;
}
- g_free(buf);
- buf = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY);
- if (buf) {
- ret = parse_redundancy(s, buf);
+ if (opts->has_redundancy) {
+ ret = parse_redundancy(s, opts->redundancy);
if (ret < 0) {
- error_setg(errp, "Invalid redundancy mode: '%s'", buf);
+ error_setg(errp, "Invalid redundancy mode");
goto out;
}
}
goto out;
}
- if (backing_file) {
+ if (opts->has_backing_file) {
BlockBackend *blk;
BDRVSheepdogState *base;
BlockDriver *drv;
/* Currently, only Sheepdog backing image is supported. */
- drv = bdrv_find_protocol(backing_file, true, NULL);
+ drv = bdrv_find_protocol(opts->backing_file, true, NULL);
if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) {
error_setg(errp, "backing_file must be a sheepdog image");
ret = -EINVAL;
goto out;
}
- blk = blk_new_open(backing_file, NULL, NULL,
+ blk = blk_new_open(opts->backing_file, NULL, NULL,
BDRV_O_PROTOCOL, errp);
if (blk == NULL) {
ret = -EIO;
}
if (prealloc) {
- BlockDriverState *bs;
- QDict *opts;
-
- opts = qdict_new();
- qdict_put_str(opts, "driver", "sheepdog");
- bs = bdrv_open(filename, NULL, opts, BDRV_O_PROTOCOL | BDRV_O_RDWR,
- errp);
- if (!bs) {
- goto out;
- }
-
- ret = sd_prealloc(bs, 0, s->inode.vdi_size, errp);
-
- bdrv_unref(bs);
+ ret = sd_create_prealloc(opts->location, opts->size, errp);
}
out:
g_free(backing_file);
g_free(buf);
+ g_free(s->addr);
g_free(s);
return ret;
}
+static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
+ Error **errp)
+{
+ BlockdevCreateOptions *create_options = NULL;
+ QDict *qdict, *location_qdict;
+ QObject *crumpled;
+ Visitor *v;
+ const char *redundancy;
+ Error *local_err = NULL;
+ int ret;
+
+ redundancy = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY);
+
+ qdict = qemu_opts_to_qdict(opts, NULL);
+ qdict_put_str(qdict, "driver", "sheepdog");
+
+ location_qdict = qdict_new();
+ qdict_put(qdict, "location", location_qdict);
+
+ sd_parse_filename(filename, location_qdict, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ qdict_flatten(qdict);
+
+ /* Change legacy command line options into QMP ones */
+ static const QDictRenames opt_renames[] = {
+ { BLOCK_OPT_BACKING_FILE, "backing-file" },
+ { BLOCK_OPT_OBJECT_SIZE, "object-size" },
+ { NULL, NULL },
+ };
+
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* Get the QAPI object */
+ crumpled = qdict_crumple(qdict, errp);
+ if (crumpled == NULL) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ v = qobject_input_visitor_new_keyval(crumpled);
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
+ visit_free(v);
+ qobject_decref(crumpled);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ assert(create_options->driver == BLOCKDEV_DRIVER_SHEEPDOG);
+ create_options->u.sheepdog.size =
+ ROUND_UP(create_options->u.sheepdog.size, BDRV_SECTOR_SIZE);
+
+ if (redundancy) {
+ create_options->u.sheepdog.has_redundancy = true;
+ create_options->u.sheepdog.redundancy =
+ parse_redundancy_str(redundancy);
+ if (create_options->u.sheepdog.redundancy == NULL) {
+ error_setg(errp, "Invalid redundancy mode");
+ ret = -EINVAL;
+ goto fail;
+ }
+ }
+
+ ret = sd_co_create(create_options, errp);
+fail:
+ qapi_free_BlockdevCreateOptions(create_options);
+ QDECREF(qdict);
+ return ret;
+}
+
static void sd_close(BlockDriverState *bs)
{
Error *local_err = NULL;
return acb.ret;
}
-static coroutine_fn int64_t
-sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
- int *pnum, BlockDriverState **file)
+static coroutine_fn int
+sd_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
+ int64_t bytes, int64_t *pnum, int64_t *map,
+ BlockDriverState **file)
{
BDRVSheepdogState *s = bs->opaque;
SheepdogInode *inode = &s->inode;
uint32_t object_size = (UINT32_C(1) << inode->block_size_shift);
- uint64_t offset = sector_num * BDRV_SECTOR_SIZE;
unsigned long start = offset / object_size,
- end = DIV_ROUND_UP((sector_num + nb_sectors) *
- BDRV_SECTOR_SIZE, object_size);
+ end = DIV_ROUND_UP(offset + bytes, object_size);
unsigned long idx;
- int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
+ *map = offset;
+ int ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
for (idx = start; idx < end; idx++) {
if (inode->data_vdi_id[idx] == 0) {
}
}
- *pnum = (idx - start) * object_size / BDRV_SECTOR_SIZE;
- if (*pnum > nb_sectors) {
- *pnum = nb_sectors;
+ *pnum = (idx - start) * object_size;
+ if (*pnum > bytes) {
+ *pnum = bytes;
}
if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) {
*file = bs;
.bdrv_reopen_commit = sd_reopen_commit,
.bdrv_reopen_abort = sd_reopen_abort,
.bdrv_close = sd_close,
- .bdrv_create = sd_create,
+ .bdrv_co_create = sd_co_create,
+ .bdrv_co_create_opts = sd_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength,
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
.bdrv_co_pdiscard = sd_co_pdiscard,
- .bdrv_co_get_block_status = sd_co_get_block_status,
+ .bdrv_co_block_status = sd_co_block_status,
.bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto,
.bdrv_reopen_commit = sd_reopen_commit,
.bdrv_reopen_abort = sd_reopen_abort,
.bdrv_close = sd_close,
- .bdrv_create = sd_create,
+ .bdrv_co_create = sd_co_create,
+ .bdrv_co_create_opts = sd_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength,
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
.bdrv_co_pdiscard = sd_co_pdiscard,
- .bdrv_co_get_block_status = sd_co_get_block_status,
+ .bdrv_co_block_status = sd_co_block_status,
.bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto,
.bdrv_reopen_commit = sd_reopen_commit,
.bdrv_reopen_abort = sd_reopen_abort,
.bdrv_close = sd_close,
- .bdrv_create = sd_create,
+ .bdrv_co_create = sd_co_create,
+ .bdrv_co_create_opts = sd_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength,
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
.bdrv_co_pdiscard = sd_co_pdiscard,
- .bdrv_co_get_block_status = sd_co_get_block_status,
+ .bdrv_co_block_status = sd_co_block_status,
.bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto,