* See the COPYING file in the top-level directory.
*
*/
+
#include "qemu/osdep.h"
#include <glusterfs/api/glfs.h>
#include "block/block_int.h"
#include "qapi/error.h"
+#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h"
#include "qemu/uri.h"
#include "qemu/error-report.h"
+#include "qemu/option.h"
#include "qemu/cutils.h"
#define GLUSTER_OPT_FILENAME "filename"
{
.name = GLUSTER_OPT_TYPE,
.type = QEMU_OPT_STRING,
- .help = "tcp|unix",
+ .help = "inet|unix",
},
{ /* end of list */ }
},
},
};
-static QemuOptsList runtime_tcp_opts = {
- .name = "gluster_tcp",
- .head = QTAILQ_HEAD_INITIALIZER(runtime_tcp_opts.head),
+static QemuOptsList runtime_inet_opts = {
+ .name = "gluster_inet",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_inet_opts.head),
.desc = {
{
.name = GLUSTER_OPT_TYPE,
.type = QEMU_OPT_STRING,
- .help = "tcp|unix",
+ .help = "inet|unix",
},
{
.name = GLUSTER_OPT_HOST,
static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
const char *filename)
{
- GlusterServer *gsconf;
+ SocketAddress *gsconf;
URI *uri;
QueryParams *qp = NULL;
bool is_unix = false;
return -EINVAL;
}
- gconf->server = g_new0(GlusterServerList, 1);
- gconf->server->value = gsconf = g_new0(GlusterServer, 1);
+ gconf->server = g_new0(SocketAddressList, 1);
+ gconf->server->value = gsconf = g_new0(SocketAddress, 1);
/* transport */
if (!uri->scheme || !strcmp(uri->scheme, "gluster")) {
- gsconf->type = GLUSTER_TRANSPORT_TCP;
+ gsconf->type = SOCKET_ADDRESS_TYPE_INET;
} else if (!strcmp(uri->scheme, "gluster+tcp")) {
- gsconf->type = GLUSTER_TRANSPORT_TCP;
+ gsconf->type = SOCKET_ADDRESS_TYPE_INET;
} else if (!strcmp(uri->scheme, "gluster+unix")) {
- gsconf->type = GLUSTER_TRANSPORT_UNIX;
+ gsconf->type = SOCKET_ADDRESS_TYPE_UNIX;
is_unix = true;
} else if (!strcmp(uri->scheme, "gluster+rdma")) {
- gsconf->type = GLUSTER_TRANSPORT_TCP;
- error_report("Warning: rdma feature is not supported, falling "
- "back to tcp");
+ gsconf->type = SOCKET_ADDRESS_TYPE_INET;
+ warn_report("rdma feature is not supported, falling back to tcp");
} else {
ret = -EINVAL;
goto out;
}
gsconf->u.q_unix.path = g_strdup(qp->p[0].value);
} else {
- gsconf->u.tcp.host = g_strdup(uri->server ? uri->server : "localhost");
+ gsconf->u.inet.host = g_strdup(uri->server ? uri->server : "localhost");
if (uri->port) {
- gsconf->u.tcp.port = g_strdup_printf("%d", uri->port);
+ gsconf->u.inet.port = g_strdup_printf("%d", uri->port);
} else {
- gsconf->u.tcp.port = g_strdup_printf("%d", GLUSTER_DEFAULT_PORT);
+ gsconf->u.inet.port = g_strdup_printf("%d", GLUSTER_DEFAULT_PORT);
}
}
struct glfs *glfs;
int ret;
int old_errno;
- GlusterServerList *server;
+ SocketAddressList *server;
unsigned long long port;
glfs = glfs_find_preopened(gconf->volume);
glfs_set_preopened(gconf->volume, glfs);
for (server = gconf->server; server; server = server->next) {
- if (server->value->type == GLUSTER_TRANSPORT_UNIX) {
- ret = glfs_set_volfile_server(glfs,
- GlusterTransport_lookup[server->value->type],
+ switch (server->value->type) {
+ case SOCKET_ADDRESS_TYPE_UNIX:
+ ret = glfs_set_volfile_server(glfs, "unix",
server->value->u.q_unix.path, 0);
- } else {
- if (parse_uint_full(server->value->u.tcp.port, &port, 10) < 0 ||
+ break;
+ case SOCKET_ADDRESS_TYPE_INET:
+ if (parse_uint_full(server->value->u.inet.port, &port, 10) < 0 ||
port > 65535) {
error_setg(errp, "'%s' is not a valid port number",
- server->value->u.tcp.port);
+ server->value->u.inet.port);
errno = EINVAL;
goto out;
}
- ret = glfs_set_volfile_server(glfs,
- GlusterTransport_lookup[server->value->type],
- server->value->u.tcp.host,
+ ret = glfs_set_volfile_server(glfs, "tcp",
+ server->value->u.inet.host,
(int)port);
+ break;
+ case SOCKET_ADDRESS_TYPE_VSOCK:
+ case SOCKET_ADDRESS_TYPE_FD:
+ default:
+ abort();
}
if (ret < 0) {
error_setg(errp, "Gluster connection for volume %s, path %s failed"
" to connect", gconf->volume, gconf->path);
for (server = gconf->server; server; server = server->next) {
- if (server->value->type == GLUSTER_TRANSPORT_UNIX) {
+ if (server->value->type == SOCKET_ADDRESS_TYPE_UNIX) {
error_append_hint(errp, "hint: failed on socket %s ",
server->value->u.q_unix.path);
} else {
error_append_hint(errp, "hint: failed on host %s and port %s ",
- server->value->u.tcp.host,
- server->value->u.tcp.port);
+ server->value->u.inet.host,
+ server->value->u.inet.port);
}
}
return NULL;
}
-static int qapi_enum_parse(const char *opt)
-{
- int i;
-
- if (!opt) {
- return GLUSTER_TRANSPORT__MAX;
- }
-
- for (i = 0; i < GLUSTER_TRANSPORT__MAX; i++) {
- if (!strcmp(opt, GlusterTransport_lookup[i])) {
- return i;
- }
- }
-
- return i;
-}
-
/*
* Convert the json formatted command line into qapi.
*/
QDict *options, Error **errp)
{
QemuOpts *opts;
- GlusterServer *gsconf;
- GlusterServerList *curr = NULL;
+ SocketAddress *gsconf = NULL;
+ SocketAddressList *curr = NULL;
QDict *backing_options = NULL;
Error *local_err = NULL;
char *str = NULL;
const char *ptr;
- size_t num_servers;
- int i;
+ int i, type, num_servers;
/* create opts info from runtime_json_opts list */
opts = qemu_opts_create(&runtime_json_opts, NULL, 0, &error_abort);
}
ptr = qemu_opt_get(opts, GLUSTER_OPT_TYPE);
- gsconf = g_new0(GlusterServer, 1);
- gsconf->type = qapi_enum_parse(ptr);
if (!ptr) {
error_setg(&local_err, QERR_MISSING_PARAMETER, GLUSTER_OPT_TYPE);
error_append_hint(&local_err, GERR_INDEX_HINT, i);
goto out;
}
- if (gsconf->type == GLUSTER_TRANSPORT__MAX) {
- error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE,
- GLUSTER_OPT_TYPE, "tcp or unix");
+ gsconf = g_new0(SocketAddress, 1);
+ if (!strcmp(ptr, "tcp")) {
+ ptr = "inet"; /* accept legacy "tcp" */
+ }
+ type = qapi_enum_parse(&SocketAddressType_lookup, ptr, -1, NULL);
+ if (type != SOCKET_ADDRESS_TYPE_INET
+ && type != SOCKET_ADDRESS_TYPE_UNIX) {
+ error_setg(&local_err,
+ "Parameter '%s' may be 'inet' or 'unix'",
+ GLUSTER_OPT_TYPE);
error_append_hint(&local_err, GERR_INDEX_HINT, i);
goto out;
}
+ gsconf->type = type;
qemu_opts_del(opts);
- if (gsconf->type == GLUSTER_TRANSPORT_TCP) {
- /* create opts info from runtime_tcp_opts list */
- opts = qemu_opts_create(&runtime_tcp_opts, NULL, 0, &error_abort);
+ if (gsconf->type == SOCKET_ADDRESS_TYPE_INET) {
+ /* create opts info from runtime_inet_opts list */
+ opts = qemu_opts_create(&runtime_inet_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, backing_options, &local_err);
if (local_err) {
goto out;
error_append_hint(&local_err, GERR_INDEX_HINT, i);
goto out;
}
- gsconf->u.tcp.host = g_strdup(ptr);
+ gsconf->u.inet.host = g_strdup(ptr);
ptr = qemu_opt_get(opts, GLUSTER_OPT_PORT);
if (!ptr) {
error_setg(&local_err, QERR_MISSING_PARAMETER,
error_append_hint(&local_err, GERR_INDEX_HINT, i);
goto out;
}
- gsconf->u.tcp.port = g_strdup(ptr);
+ gsconf->u.inet.port = g_strdup(ptr);
/* defend for unsupported fields in InetSocketAddress,
* i.e. @ipv4, @ipv6 and @to
*/
ptr = qemu_opt_get(opts, GLUSTER_OPT_TO);
if (ptr) {
- gsconf->u.tcp.has_to = true;
+ gsconf->u.inet.has_to = true;
}
ptr = qemu_opt_get(opts, GLUSTER_OPT_IPV4);
if (ptr) {
- gsconf->u.tcp.has_ipv4 = true;
+ gsconf->u.inet.has_ipv4 = true;
}
ptr = qemu_opt_get(opts, GLUSTER_OPT_IPV6);
if (ptr) {
- gsconf->u.tcp.has_ipv6 = true;
+ gsconf->u.inet.has_ipv6 = true;
}
- if (gsconf->u.tcp.has_to) {
+ if (gsconf->u.inet.has_to) {
error_setg(&local_err, "Parameter 'to' not supported");
goto out;
}
- if (gsconf->u.tcp.has_ipv4 || gsconf->u.tcp.has_ipv6) {
+ if (gsconf->u.inet.has_ipv4 || gsconf->u.inet.has_ipv6) {
error_setg(&local_err, "Parameters 'ipv4/ipv6' not supported");
goto out;
}
}
if (gconf->server == NULL) {
- gconf->server = g_new0(GlusterServerList, 1);
+ gconf->server = g_new0(SocketAddressList, 1);
gconf->server->value = gsconf;
curr = gconf->server;
} else {
- curr->next = g_new0(GlusterServerList, 1);
+ curr->next = g_new0(SocketAddressList, 1);
curr->next->value = gsconf;
curr = curr->next;
}
+ gsconf = NULL;
- qdict_del(backing_options, str);
+ QDECREF(backing_options);
+ backing_options = NULL;
g_free(str);
str = NULL;
}
out:
error_propagate(errp, local_err);
+ qapi_free_SocketAddress(gsconf);
qemu_opts_del(opts);
- if (str) {
- qdict_del(backing_options, str);
- g_free(str);
- }
+ g_free(str);
+ QDECREF(backing_options);
errno = EINVAL;
return -errno;
}
"file.volume=testvol,file.path=/path/a.qcow2"
"[,file.debug=9]"
"[,file.logfile=/path/filename.log],"
- "file.server.0.type=tcp,"
+ "file.server.0.type=inet,"
"file.server.0.host=1.2.3.4,"
"file.server.0.port=24007,"
"file.server.1.transport=unix,"
return qemu_gluster_glfs_init(gconf, errp);
}
-static void qemu_gluster_complete_aio(void *opaque)
-{
- GlusterAIOCB *acb = (GlusterAIOCB *)opaque;
-
- qemu_coroutine_enter(acb->coroutine);
-}
-
/*
* AIO callback routine called from GlusterFS thread.
*/
acb->ret = -EIO; /* Partial read/write - fail it */
}
- aio_bh_schedule_oneshot(acb->aio_context, qemu_gluster_complete_aio, acb);
+ aio_co_schedule(acb->aio_context, acb->coroutine);
}
static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags)
qemu_coroutine_yield();
return acb.ret;
}
+#endif
-static inline bool gluster_supports_zerofill(void)
+static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset,
+ PreallocMode prealloc, Error **errp)
{
- return 1;
-}
+ int64_t current_length;
-static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
- int64_t size)
-{
- return glfs_zerofill(fd, offset, size);
-}
+ current_length = glfs_lseek(fd, 0, SEEK_END);
+ if (current_length < 0) {
+ error_setg_errno(errp, errno, "Failed to determine current size");
+ return -errno;
+ }
-#else
-static inline bool gluster_supports_zerofill(void)
-{
- return 0;
-}
+ if (current_length > offset && prealloc != PREALLOC_MODE_OFF) {
+ error_setg(errp, "Cannot use preallocation for shrinking files");
+ return -ENOTSUP;
+ }
+
+ if (current_length == offset) {
+ return 0;
+ }
+
+ switch (prealloc) {
+#ifdef CONFIG_GLUSTERFS_FALLOCATE
+ case PREALLOC_MODE_FALLOC:
+ if (glfs_fallocate(fd, 0, current_length, offset - current_length)) {
+ error_setg_errno(errp, errno, "Could not preallocate data");
+ return -errno;
+ }
+ break;
+#endif /* CONFIG_GLUSTERFS_FALLOCATE */
+#ifdef CONFIG_GLUSTERFS_ZEROFILL
+ case PREALLOC_MODE_FULL:
+ if (glfs_ftruncate(fd, offset)) {
+ error_setg_errno(errp, errno, "Could not resize file");
+ return -errno;
+ }
+ if (glfs_zerofill(fd, current_length, offset - current_length)) {
+ error_setg_errno(errp, errno, "Could not zerofill the new area");
+ return -errno;
+ }
+ break;
+#endif /* CONFIG_GLUSTERFS_ZEROFILL */
+ case PREALLOC_MODE_OFF:
+ if (glfs_ftruncate(fd, offset)) {
+ error_setg_errno(errp, errno, "Could not resize file");
+ return -errno;
+ }
+ break;
+ default:
+ error_setg(errp, "Unsupported preallocation mode: %s",
+ PreallocMode_str(prealloc));
+ return -EINVAL;
+ }
-static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
- int64_t size)
-{
return 0;
}
-#endif
static int qemu_gluster_create(const char *filename,
QemuOpts *opts, Error **errp)
{
BlockdevOptionsGluster *gconf;
struct glfs *glfs;
- struct glfs_fd *fd;
+ struct glfs_fd *fd = NULL;
int ret = 0;
- int prealloc = 0;
+ PreallocMode prealloc;
int64_t total_size = 0;
char *tmp = NULL;
+ Error *local_err = NULL;
gconf = g_new0(BlockdevOptionsGluster, 1);
gconf->debug = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG,
BDRV_SECTOR_SIZE);
tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
- if (!tmp || !strcmp(tmp, "off")) {
- prealloc = 0;
- } else if (!strcmp(tmp, "full") && gluster_supports_zerofill()) {
- prealloc = 1;
- } else {
- error_setg(errp, "Invalid preallocation mode: '%s'"
- " or GlusterFS doesn't support zerofill API", tmp);
+ prealloc = qapi_enum_parse(&PreallocMode_lookup, tmp, PREALLOC_MODE_OFF,
+ &local_err);
+ g_free(tmp);
+ if (local_err) {
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto out;
}
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
if (!fd) {
ret = -errno;
- } else {
- if (!glfs_ftruncate(fd, total_size)) {
- if (prealloc && qemu_gluster_zerofill(fd, 0, total_size)) {
- ret = -errno;
- }
- } else {
- ret = -errno;
- }
+ goto out;
+ }
+
+ ret = qemu_gluster_do_truncate(fd, total_size, prealloc, errp);
- if (glfs_close(fd) != 0) {
+out:
+ if (fd) {
+ if (glfs_close(fd) != 0 && ret == 0) {
ret = -errno;
}
}
-out:
- g_free(tmp);
qapi_free_BlockdevOptionsGluster(gconf);
glfs_clear_preopened(glfs);
return ret;
return acb.ret;
}
-static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset)
+static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset,
+ PreallocMode prealloc, Error **errp)
{
- int ret;
BDRVGlusterState *s = bs->opaque;
-
- ret = glfs_ftruncate(s->fd, offset);
- if (ret < 0) {
- return -errno;
- }
-
- return 0;
+ return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp);
}
static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,
if (offs < 0) {
return -errno; /* D3 or D4 */
}
- assert(offs >= start);
+
+ if (offs < start) {
+ /* This is not a valid return by lseek(). We are safe to just return
+ * -EIO in this case, and we'll treat it like D4. Unfortunately some
+ * versions of gluster server will return offs < start, so an assert
+ * here will unnecessarily abort QEMU. */
+ return -EIO;
+ }
if (offs > start) {
/* D2: in hole, next data at offs */
if (offs < 0) {
return -errno; /* D1 and (H3 or H4) */
}
- assert(offs >= start);
+
+ if (offs < start) {
+ /* This is not a valid return by lseek(). We are safe to just return
+ * -EIO in this case, and we'll treat it like H4. Unfortunately some
+ * versions of gluster server will return offs < start, so an assert
+ * here will unnecessarily abort QEMU. */
+ return -EIO;
+ }
if (offs > start) {
/*
}
/*
- * Returns the allocation status of the specified sectors.
+ * Returns the allocation status of the specified offset.
*
- * If 'sector_num' is beyond the end of the disk image the return value is 0
- * and 'pnum' is set to 0.
+ * The block layer guarantees 'offset' and 'bytes' are within bounds.
*
- * 'pnum' is set to the number of sectors (including and immediately following
- * the specified sector) that are known to be in the same
+ * 'pnum' is set to the number of bytes (including and immediately following
+ * the specified offset) that are known to be in the same
* allocated/unallocated state.
*
- * 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes
- * beyond the end of the disk image it will be clamped.
+ * 'bytes' is the max value 'pnum' should be set to.
*
- * (Based on raw_co_get_block_status() from file-posix.c.)
+ * (Based on raw_co_block_status() from file-posix.c.)
*/
-static int64_t coroutine_fn qemu_gluster_co_get_block_status(
- BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
- BlockDriverState **file)
+static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs,
+ bool want_zero,
+ int64_t offset,
+ int64_t bytes,
+ int64_t *pnum,
+ int64_t *map,
+ BlockDriverState **file)
{
BDRVGlusterState *s = bs->opaque;
- off_t start, data = 0, hole = 0;
- int64_t total_size;
+ off_t data = 0, hole = 0;
int ret = -EINVAL;
if (!s->fd) {
return ret;
}
- start = sector_num * BDRV_SECTOR_SIZE;
- total_size = bdrv_getlength(bs);
- if (total_size < 0) {
- return total_size;
- } else if (start >= total_size) {
- *pnum = 0;
- return 0;
- } else if (start + nb_sectors * BDRV_SECTOR_SIZE > total_size) {
- nb_sectors = DIV_ROUND_UP(total_size - start, BDRV_SECTOR_SIZE);
+ if (!want_zero) {
+ *pnum = bytes;
+ *map = offset;
+ *file = bs;
+ return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
}
- ret = find_allocation(bs, start, &data, &hole);
+ ret = find_allocation(bs, offset, &data, &hole);
if (ret == -ENXIO) {
/* Trailing hole */
- *pnum = nb_sectors;
+ *pnum = bytes;
ret = BDRV_BLOCK_ZERO;
} else if (ret < 0) {
/* No info available, so pretend there are no holes */
- *pnum = nb_sectors;
+ *pnum = bytes;
ret = BDRV_BLOCK_DATA;
- } else if (data == start) {
- /* On a data extent, compute sectors to the end of the extent,
+ } else if (data == offset) {
+ /* On a data extent, compute bytes to the end of the extent,
* possibly including a partial sector at EOF. */
- *pnum = MIN(nb_sectors, DIV_ROUND_UP(hole - start, BDRV_SECTOR_SIZE));
+ *pnum = MIN(bytes, hole - offset);
ret = BDRV_BLOCK_DATA;
} else {
- /* On a hole, compute sectors to the beginning of the next extent. */
- assert(hole == start);
- *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE);
+ /* On a hole, compute bytes to the beginning of the next extent. */
+ assert(hole == offset);
+ *pnum = MIN(bytes, data - offset);
ret = BDRV_BLOCK_ZERO;
}
+ *map = offset;
*file = bs;
- return ret | BDRV_BLOCK_OFFSET_VALID | start;
+ return ret | BDRV_BLOCK_OFFSET_VALID;
}
#ifdef CONFIG_GLUSTERFS_ZEROFILL
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
#endif
- .bdrv_co_get_block_status = qemu_gluster_co_get_block_status,
+ .bdrv_co_block_status = qemu_gluster_co_block_status,
.create_opts = &qemu_gluster_create_opts,
};
#ifdef CONFIG_GLUSTERFS_ZEROFILL
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
#endif
- .bdrv_co_get_block_status = qemu_gluster_co_get_block_status,
+ .bdrv_co_block_status = qemu_gluster_co_block_status,
.create_opts = &qemu_gluster_create_opts,
};
#ifdef CONFIG_GLUSTERFS_ZEROFILL
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
#endif
- .bdrv_co_get_block_status = qemu_gluster_co_get_block_status,
+ .bdrv_co_block_status = qemu_gluster_co_block_status,
.create_opts = &qemu_gluster_create_opts,
};
#ifdef CONFIG_GLUSTERFS_ZEROFILL
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
#endif
- .bdrv_co_get_block_status = qemu_gluster_co_get_block_status,
+ .bdrv_co_block_status = qemu_gluster_co_block_status,
.create_opts = &qemu_gluster_create_opts,
};