*/
#include "qemu/osdep.h"
+#include "qemu/units.h"
#include <glusterfs/api/glfs.h>
#include "block/block_int.h"
#include "block/qdict.h"
#include "qapi/qmp/qerror.h"
#include "qemu/uri.h"
#include "qemu/error-report.h"
+#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/cutils.h"
+#ifdef CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT
+# define glfs_ftruncate(fd, offset) glfs_ftruncate(fd, offset, NULL, NULL)
+#endif
+
#define GLUSTER_OPT_FILENAME "filename"
#define GLUSTER_OPT_VOLUME "volume"
#define GLUSTER_OPT_PATH "path"
#define GLUSTER_DEBUG_MAX 9
#define GLUSTER_OPT_LOGFILE "logfile"
#define GLUSTER_LOGFILE_DEFAULT "-" /* handled in libgfapi as /dev/stderr */
+/*
+ * Several versions of GlusterFS (3.12? -> 6.0.1) fail when the transfer size
+ * is greater or equal to 1024 MiB, so we are limiting the transfer size to 512
+ * MiB to avoid this rare issue.
+ */
+#define GLUSTER_MAX_TRANSFER (512 * MiB)
#define GERR_INDEX_HINT "hint: check in 'server' array index '%d'\n"
GlfsPreopened saved;
} ListElement;
-static QLIST_HEAD(glfs_list, ListElement) glfs_list;
+static QLIST_HEAD(, ListElement) glfs_list;
static QemuOptsList qemu_gluster_create_opts = {
.name = "qemu-gluster-create-opts",
{
.name = BLOCK_OPT_PREALLOC,
.type = QEMU_OPT_STRING,
- .help = "Preallocation mode (allowed values: off, full)"
+ .help = "Preallocation mode (allowed values: off"
+#ifdef CONFIG_GLUSTERFS_FALLOCATE
+ ", falloc"
+#endif
+#ifdef CONFIG_GLUSTERFS_ZEROFILL
+ ", full"
+#endif
+ ")"
},
{
.name = GLUSTER_OPT_DEBUG,
/* create opts info from runtime_json_opts list */
opts = qemu_opts_create(&runtime_json_opts, NULL, 0, &error_abort);
- qemu_opts_absorb_qdict(opts, options, &local_err);
- if (local_err) {
+ if (!qemu_opts_absorb_qdict(opts, options, errp)) {
goto out;
}
/* create opts info from runtime_type_opts list */
opts = qemu_opts_create(&runtime_type_opts, NULL, 0, &error_abort);
- qemu_opts_absorb_qdict(opts, backing_options, &local_err);
- if (local_err) {
+ if (!qemu_opts_absorb_qdict(opts, backing_options, errp)) {
goto out;
}
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) {
+ if (!qemu_opts_absorb_qdict(opts, backing_options, errp)) {
goto out;
}
} else {
/* create opts info from runtime_unix_opts list */
opts = qemu_opts_create(&runtime_unix_opts, NULL, 0, &error_abort);
- qemu_opts_absorb_qdict(opts, backing_options, &local_err);
- if (local_err) {
+ if (!qemu_opts_absorb_qdict(opts, backing_options, errp)) {
goto out;
}
/*
* AIO callback routine called from GlusterFS thread.
*/
-static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
+static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret,
+#ifdef CONFIG_GLUSTERFS_IOCB_HAS_STAT
+ struct glfs_stat *pre, struct glfs_stat *post,
+#endif
+ void *arg)
{
GlusterAIOCB *acb = (GlusterAIOCB *)arg;
int ret = 0;
BlockdevOptionsGluster *gconf = NULL;
QemuOpts *opts;
- Error *local_err = NULL;
const char *filename, *logfile;
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
- qemu_opts_absorb_qdict(opts, options, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!qemu_opts_absorb_qdict(opts, options, errp)) {
ret = -EINVAL;
goto out;
}
qemu_gluster_parse_flags(bdrv_flags, &open_flags);
s->fd = glfs_open(s->glfs, gconf->path, open_flags);
- if (!s->fd) {
- ret = -errno;
+ ret = s->fd ? 0 : -errno;
+
+ if (ret == -EACCES || ret == -EROFS) {
+ /* Try to degrade to read-only, but if it doesn't work, still use the
+ * normal error message. */
+ if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
+ open_flags = (open_flags & ~O_RDWR) | O_RDONLY;
+ s->fd = glfs_open(s->glfs, gconf->path, open_flags);
+ ret = s->fd ? 0 : -errno;
+ }
}
s->supports_seek_data = qemu_gluster_test_seek(s->fd);
return ret;
}
+static void qemu_gluster_refresh_limits(BlockDriverState *bs, Error **errp)
+{
+ bs->bl.max_transfer = GLUSTER_MAX_TRANSFER;
+}
+
static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
BlockReopenQueue *queue, Error **errp)
{
gconf->has_debug = true;
gconf->logfile = g_strdup(s->logfile);
gconf->has_logfile = true;
- reop_s->glfs = qemu_gluster_init(gconf, state->bs->filename, NULL, errp);
+
+ /*
+ * If 'state->bs->exact_filename' is empty, 'state->options' should contain
+ * the JSON parameters already parsed.
+ */
+ if (state->bs->exact_filename[0] != '\0') {
+ reop_s->glfs = qemu_gluster_init(gconf, state->bs->exact_filename, NULL,
+ errp);
+ } else {
+ reop_s->glfs = qemu_gluster_init(gconf, NULL, state->options, errp);
+ }
if (reop_s->glfs == NULL) {
ret = -errno;
goto exit;
return ret;
}
-static int coroutine_fn qemu_gluster_co_create_opts(const char *filename,
+static int coroutine_fn qemu_gluster_co_create_opts(BlockDriver *drv,
+ const char *filename,
QemuOpts *opts,
Error **errp)
{
static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs,
int64_t offset,
+ bool exact,
PreallocMode prealloc,
+ BdrvRequestFlags flags,
Error **errp)
{
BDRVGlusterState *s = bs->opaque;
}
}
-static int qemu_gluster_has_zero_init(BlockDriverState *bs)
-{
- /* GlusterFS volume could be backed by a block device */
- return 0;
-}
-
/*
* Find allocation range in @bs around offset @start.
* May change underlying file descriptor's file offset.
}
+static const char *const gluster_strong_open_opts[] = {
+ GLUSTER_OPT_VOLUME,
+ GLUSTER_OPT_PATH,
+ GLUSTER_OPT_TYPE,
+ GLUSTER_OPT_SERVER_PATTERN,
+ GLUSTER_OPT_HOST,
+ GLUSTER_OPT_PORT,
+ GLUSTER_OPT_TO,
+ GLUSTER_OPT_IPV4,
+ GLUSTER_OPT_IPV6,
+ GLUSTER_OPT_SOCKET,
+
+ NULL
+};
+
static BlockDriver bdrv_gluster = {
.format_name = "gluster",
.protocol_name = "gluster",
.bdrv_co_readv = qemu_gluster_co_readv,
.bdrv_co_writev = qemu_gluster_co_writev,
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
- .bdrv_has_zero_init = qemu_gluster_has_zero_init,
#ifdef CONFIG_GLUSTERFS_DISCARD
.bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
#endif
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
#endif
.bdrv_co_block_status = qemu_gluster_co_block_status,
+ .bdrv_refresh_limits = qemu_gluster_refresh_limits,
.create_opts = &qemu_gluster_create_opts,
+ .strong_runtime_opts = gluster_strong_open_opts,
};
static BlockDriver bdrv_gluster_tcp = {
.bdrv_co_readv = qemu_gluster_co_readv,
.bdrv_co_writev = qemu_gluster_co_writev,
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
- .bdrv_has_zero_init = qemu_gluster_has_zero_init,
#ifdef CONFIG_GLUSTERFS_DISCARD
.bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
#endif
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
#endif
.bdrv_co_block_status = qemu_gluster_co_block_status,
+ .bdrv_refresh_limits = qemu_gluster_refresh_limits,
.create_opts = &qemu_gluster_create_opts,
+ .strong_runtime_opts = gluster_strong_open_opts,
};
static BlockDriver bdrv_gluster_unix = {
.bdrv_co_readv = qemu_gluster_co_readv,
.bdrv_co_writev = qemu_gluster_co_writev,
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
- .bdrv_has_zero_init = qemu_gluster_has_zero_init,
#ifdef CONFIG_GLUSTERFS_DISCARD
.bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
#endif
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
#endif
.bdrv_co_block_status = qemu_gluster_co_block_status,
+ .bdrv_refresh_limits = qemu_gluster_refresh_limits,
.create_opts = &qemu_gluster_create_opts,
+ .strong_runtime_opts = gluster_strong_open_opts,
};
/* rdma is deprecated (actually never supported for volfile fetch).
.bdrv_co_readv = qemu_gluster_co_readv,
.bdrv_co_writev = qemu_gluster_co_writev,
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
- .bdrv_has_zero_init = qemu_gluster_has_zero_init,
#ifdef CONFIG_GLUSTERFS_DISCARD
.bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
#endif
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
#endif
.bdrv_co_block_status = qemu_gluster_co_block_status,
+ .bdrv_refresh_limits = qemu_gluster_refresh_limits,
.create_opts = &qemu_gluster_create_opts,
+ .strong_runtime_opts = gluster_strong_open_opts,
};
static void bdrv_gluster_init(void)