#include "qapi/qmp/qerror.h"
#include "qemu/uri.h"
#include "qemu/error-report.h"
+#include "qemu/cutils.h"
#define GLUSTER_OPT_FILENAME "filename"
#define GLUSTER_OPT_VOLUME "volume"
#define GLUSTER_DEFAULT_PORT 24007
#define GLUSTER_DEBUG_DEFAULT 4
#define GLUSTER_DEBUG_MAX 9
+#define GLUSTER_OPT_LOGFILE "logfile"
+#define GLUSTER_LOGFILE_DEFAULT "-" /* handled in libgfapi as /dev/stderr */
#define GERR_INDEX_HINT "hint: check in 'server' array index '%d'\n"
typedef struct GlusterAIOCB {
int64_t size;
int ret;
- QEMUBH *bh;
Coroutine *coroutine;
AioContext *aio_context;
} GlusterAIOCB;
typedef struct BDRVGlusterState {
struct glfs *glfs;
struct glfs_fd *fd;
+ char *logfile;
bool supports_seek_data;
int debug_level;
} BDRVGlusterState;
} BDRVGlusterReopenState;
+typedef struct GlfsPreopened {
+ char *volume;
+ glfs_t *fs;
+ int ref;
+} GlfsPreopened;
+
+typedef struct ListElement {
+ QLIST_ENTRY(ListElement) list;
+ GlfsPreopened saved;
+} ListElement;
+
+static QLIST_HEAD(glfs_list, ListElement) glfs_list;
+
static QemuOptsList qemu_gluster_create_opts = {
.name = "qemu-gluster-create-opts",
.head = QTAILQ_HEAD_INITIALIZER(qemu_gluster_create_opts.head),
.type = QEMU_OPT_NUMBER,
.help = "Gluster log level, valid range is 0-9",
},
+ {
+ .name = GLUSTER_OPT_LOGFILE,
+ .type = QEMU_OPT_STRING,
+ .help = "Logfile path of libgfapi",
+ },
{ /* end of list */ }
}
};
.type = QEMU_OPT_NUMBER,
.help = "Gluster log level, valid range is 0-9",
},
+ {
+ .name = GLUSTER_OPT_LOGFILE,
+ .type = QEMU_OPT_STRING,
+ .help = "Logfile path of libgfapi",
+ },
{ /* end of list */ }
},
};
},
{
.name = GLUSTER_OPT_PORT,
- .type = QEMU_OPT_NUMBER,
+ .type = QEMU_OPT_STRING,
.help = "port number on which glusterd is listening (default 24007)",
},
{
},
};
+static void glfs_set_preopened(const char *volume, glfs_t *fs)
+{
+ ListElement *entry = NULL;
+
+ entry = g_new(ListElement, 1);
+
+ entry->saved.volume = g_strdup(volume);
+
+ entry->saved.fs = fs;
+ entry->saved.ref = 1;
+
+ QLIST_INSERT_HEAD(&glfs_list, entry, list);
+}
+
+static glfs_t *glfs_find_preopened(const char *volume)
+{
+ ListElement *entry = NULL;
+
+ QLIST_FOREACH(entry, &glfs_list, list) {
+ if (strcmp(entry->saved.volume, volume) == 0) {
+ entry->saved.ref++;
+ return entry->saved.fs;
+ }
+ }
+
+ return NULL;
+}
+
+static void glfs_clear_preopened(glfs_t *fs)
+{
+ ListElement *entry = NULL;
+ ListElement *next;
+
+ if (fs == NULL) {
+ return;
+ }
+
+ QLIST_FOREACH_SAFE(entry, &glfs_list, list, next) {
+ if (entry->saved.fs == fs) {
+ if (--entry->saved.ref) {
+ return;
+ }
+
+ QLIST_REMOVE(entry, list);
+
+ glfs_fini(entry->saved.fs);
+ g_free(entry->saved.volume);
+ g_free(entry);
+ }
+ }
+}
+
static int parse_volume_options(BlockdevOptionsGluster *gconf, char *path)
{
char *p, *q;
int ret;
int old_errno;
GlusterServerList *server;
+ unsigned long long port;
+
+ glfs = glfs_find_preopened(gconf->volume);
+ if (glfs) {
+ return glfs;
+ }
glfs = glfs_new(gconf->volume);
if (!glfs) {
goto out;
}
+ 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],
server->value->u.q_unix.path, 0);
} else {
+ if (parse_uint_full(server->value->u.tcp.port, &port, 10) < 0 ||
+ port > 65535) {
+ error_setg(errp, "'%s' is not a valid port number",
+ server->value->u.tcp.port);
+ errno = EINVAL;
+ goto out;
+ }
ret = glfs_set_volfile_server(glfs,
GlusterTransport_lookup[server->value->type],
server->value->u.tcp.host,
- atoi(server->value->u.tcp.port));
+ (int)port);
}
if (ret < 0) {
}
}
- ret = glfs_set_logging(glfs, "-", gconf->debug_level);
+ ret = glfs_set_logging(glfs, gconf->logfile, gconf->debug_level);
if (ret < 0) {
goto out;
}
out:
if (glfs) {
old_errno = errno;
- glfs_fini(glfs);
+ glfs_clear_preopened(glfs);
errno = old_errno;
}
return NULL;
if (ret < 0) {
error_setg(errp, "invalid URI");
error_append_hint(errp, "Usage: file=gluster[+transport]://"
- "[host[:port]]/volume/path[?socket=...]\n");
+ "[host[:port]]volume/path[?socket=...]"
+ "[,file.debug=N]"
+ "[,file.logfile=/path/filename.log]\n");
errno = -ret;
return NULL;
}
error_append_hint(errp, "Usage: "
"-drive driver=qcow2,file.driver=gluster,"
"file.volume=testvol,file.path=/path/a.qcow2"
- "[,file.debug=9],file.server.0.type=tcp,"
+ "[,file.debug=9]"
+ "[,file.logfile=/path/filename.log],"
+ "file.server.0.type=tcp,"
"file.server.0.host=1.2.3.4,"
"file.server.0.port=24007,"
"file.server.1.transport=unix,"
{
GlusterAIOCB *acb = (GlusterAIOCB *)opaque;
- qemu_bh_delete(acb->bh);
- acb->bh = NULL;
qemu_coroutine_enter(acb->coroutine);
}
acb->ret = -EIO; /* Partial read/write - fail it */
}
- acb->bh = aio_bh_new(acb->aio_context, qemu_gluster_complete_aio, acb);
- qemu_bh_schedule(acb->bh);
+ aio_bh_schedule_oneshot(acb->aio_context, qemu_gluster_complete_aio, acb);
}
static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags)
*/
static bool qemu_gluster_test_seek(struct glfs_fd *fd)
{
- off_t ret, eof;
+ off_t ret = 0;
+
+#if defined SEEK_HOLE && defined SEEK_DATA
+ off_t eof;
eof = glfs_lseek(fd, 0, SEEK_END);
if (eof < 0) {
/* this should always fail with ENXIO if SEEK_DATA is supported */
ret = glfs_lseek(fd, eof, SEEK_DATA);
+#endif
+
return (ret < 0) && (errno == ENXIO);
}
BlockdevOptionsGluster *gconf = NULL;
QemuOpts *opts;
Error *local_err = NULL;
- const char *filename;
+ const char *filename, *logfile;
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
gconf = g_new0(BlockdevOptionsGluster, 1);
gconf->debug_level = s->debug_level;
gconf->has_debug_level = true;
+
+ logfile = qemu_opt_get(opts, GLUSTER_OPT_LOGFILE);
+ s->logfile = g_strdup(logfile ? logfile : GLUSTER_LOGFILE_DEFAULT);
+
+ gconf->logfile = g_strdup(s->logfile);
+ gconf->has_logfile = true;
+
s->glfs = qemu_gluster_init(gconf, filename, options, errp);
if (!s->glfs) {
ret = -errno;
if (!ret) {
return ret;
}
+ g_free(s->logfile);
if (s->fd) {
glfs_close(s->fd);
}
- if (s->glfs) {
- glfs_fini(s->glfs);
- }
+
+ glfs_clear_preopened(s->glfs);
+
return ret;
}
gconf = g_new0(BlockdevOptionsGluster, 1);
gconf->debug_level = s->debug_level;
gconf->has_debug_level = true;
+ gconf->logfile = g_strdup(s->logfile);
+ gconf->has_logfile = true;
reop_s->glfs = qemu_gluster_init(gconf, state->bs->filename, NULL, errp);
if (reop_s->glfs == NULL) {
ret = -errno;
if (s->fd) {
glfs_close(s->fd);
}
- if (s->glfs) {
- glfs_fini(s->glfs);
- }
+
+ glfs_clear_preopened(s->glfs);
/* use the newly opened image / connection */
s->fd = reop_s->fd;
glfs_close(reop_s->fd);
}
- if (reop_s->glfs) {
- glfs_fini(reop_s->glfs);
- }
+ glfs_clear_preopened(reop_s->glfs);
g_free(state->opaque);
state->opaque = NULL;
}
gconf->has_debug_level = true;
+ gconf->logfile = qemu_opt_get_del(opts, GLUSTER_OPT_LOGFILE);
+ if (!gconf->logfile) {
+ gconf->logfile = g_strdup(GLUSTER_LOGFILE_DEFAULT);
+ }
+ gconf->has_logfile = true;
+
glfs = qemu_gluster_init(gconf, filename, NULL, errp);
if (!glfs) {
ret = -errno;
out:
g_free(tmp);
qapi_free_BlockdevOptionsGluster(gconf);
- if (glfs) {
- glfs_fini(glfs);
- }
+ glfs_clear_preopened(glfs);
return ret;
}
{
BDRVGlusterState *s = bs->opaque;
+ g_free(s->logfile);
if (s->fd) {
glfs_close(s->fd);
s->fd = NULL;
}
- glfs_fini(s->glfs);
+ glfs_clear_preopened(s->glfs);
}
static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs)
}
#ifdef CONFIG_GLUSTERFS_DISCARD
-static coroutine_fn int qemu_gluster_co_discard(BlockDriverState *bs,
- int64_t sector_num,
- int nb_sectors)
+static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs,
+ int64_t offset, int size)
{
int ret;
GlusterAIOCB acb;
BDRVGlusterState *s = bs->opaque;
- size_t size = nb_sectors * BDRV_SECTOR_SIZE;
- off_t offset = sector_num * BDRV_SECTOR_SIZE;
acb.size = 0;
acb.ret = 0;
off_t *data, off_t *hole)
{
BDRVGlusterState *s = bs->opaque;
- off_t offs;
if (!s->supports_seek_data) {
- return -ENOTSUP;
+ goto exit;
}
+#if defined SEEK_HOLE && defined SEEK_DATA
+ off_t offs;
+
/*
* SEEK_DATA cases:
* D1. offs == start: start is in data
/* D1 and H1 */
return -EBUSY;
+#endif
+
+exit:
+ return -ENOTSUP;
}
/*
.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_discard = qemu_gluster_co_discard,
+ .bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
#endif
#ifdef CONFIG_GLUSTERFS_ZEROFILL
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
.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_discard = qemu_gluster_co_discard,
+ .bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
#endif
#ifdef CONFIG_GLUSTERFS_ZEROFILL
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
.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_discard = qemu_gluster_co_discard,
+ .bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
#endif
#ifdef CONFIG_GLUSTERFS_ZEROFILL
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
.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_discard = qemu_gluster_co_discard,
+ .bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
#endif
#ifdef CONFIG_GLUSTERFS_ZEROFILL
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,