* [Qemu-devel] [PATCH v8 1/6] sockets: publish dummy_opts
2012-09-24 8:51 [Qemu-devel] [PATCH v8 0/6] GlusterFS support in QEMU - v8 Bharata B Rao
@ 2012-09-24 8:52 ` Bharata B Rao
2012-09-24 8:53 ` [Qemu-devel] [PATCH v8 2/6] sockets: Make port specification optional in inet_parse Bharata B Rao
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Bharata B Rao @ 2012-09-24 8:52 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Anthony Liguori, Anand Avati, Vijay Bellur,
Stefan Hajnoczi, Harsh Bora, Amar Tumballi, Markus Armbruster,
Blue Swirl, Avi Kivity, Paolo Bonzini
sockets: publish dummy_opts
From: Paolo Bonzini <pbonzini@redhat.com>
This is needed so that we can set up a QemuOpts instance from QMP
parameters. The way to go here is to and move qemu-sockets.c away from
QemuOpts and use Laszlo's QemuOptsVisitor whenever *_opts functions are
called now. This can be done later, however.
This would also enable block backends like gluster to make use of
inet_parse() for inet address parsing.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Bharata B Rao <bharata@linux.vnet.ibm.com>
---
qemu-sockets.c | 14 +++++++-------
qemu_socket.h | 2 ++
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/qemu-sockets.c b/qemu-sockets.c
index 361d890..b292311 100644
--- a/qemu-sockets.c
+++ b/qemu-sockets.c
@@ -32,9 +32,9 @@
static const int on=1, off=0;
/* used temporarely until all users are converted to QemuOpts */
-static QemuOptsList dummy_opts = {
- .name = "dummy",
- .head = QTAILQ_HEAD_INITIALIZER(dummy_opts.head),
+QemuOptsList socket_opts = {
+ .name = "socket",
+ .head = QTAILQ_HEAD_INITIALIZER(socket_opts.head),
.desc = {
{
.name = "path",
@@ -469,7 +469,7 @@ int inet_listen(const char *str, char *ostr, int olen,
char *optstr;
int sock = -1;
- opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
+ opts = qemu_opts_create(&socket_opts, NULL, 0, NULL);
if (inet_parse(opts, str) == 0) {
sock = inet_listen_opts(opts, port_offset, errp);
if (sock != -1 && ostr) {
@@ -498,7 +498,7 @@ int inet_connect(const char *str, bool block, bool *in_progress, Error **errp)
QemuOpts *opts;
int sock = -1;
- opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
+ opts = qemu_opts_create(&socket_opts, NULL, 0, NULL);
if (inet_parse(opts, str) == 0) {
if (block) {
qemu_opt_set(opts, "block", "on");
@@ -597,7 +597,7 @@ int unix_listen(const char *str, char *ostr, int olen)
char *path, *optstr;
int sock, len;
- opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
+ opts = qemu_opts_create(&socket_opts, NULL, 0, NULL);
optstr = strchr(str, ',');
if (optstr) {
@@ -625,7 +625,7 @@ int unix_connect(const char *path)
QemuOpts *opts;
int sock;
- opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
+ opts = qemu_opts_create(&socket_opts, NULL, 0, NULL);
qemu_opt_set(opts, "path", path);
sock = unix_connect_opts(opts);
qemu_opts_del(opts);
diff --git a/qemu_socket.h b/qemu_socket.h
index 30ae6af..c87ee57 100644
--- a/qemu_socket.h
+++ b/qemu_socket.h
@@ -30,6 +30,8 @@ int inet_aton(const char *cp, struct in_addr *ia);
#include "error.h"
#include "qerror.h"
+extern QemuOptsList socket_opts;
+
/* misc helpers */
int qemu_socket(int domain, int type, int protocol);
int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH v8 2/6] sockets: Make port specification optional in inet_parse
2012-09-24 8:51 [Qemu-devel] [PATCH v8 0/6] GlusterFS support in QEMU - v8 Bharata B Rao
2012-09-24 8:52 ` [Qemu-devel] [PATCH v8 1/6] sockets: publish dummy_opts Bharata B Rao
@ 2012-09-24 8:53 ` Bharata B Rao
2012-09-24 8:54 ` [Qemu-devel] [PATCH v8 3/6] sockets: Make inet_parse() non static Bharata B Rao
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Bharata B Rao @ 2012-09-24 8:53 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Anthony Liguori, Anand Avati, Vijay Bellur,
Stefan Hajnoczi, Harsh Bora, Amar Tumballi, Markus Armbruster,
Blue Swirl, Avi Kivity, Paolo Bonzini
sockets: Make port specification optional in inet_parse
From: Bharata B Rao <bharata@linux.vnet.ibm.com>
Change inet_parse() to work without explicit port specification. Add a
default_port argument to be used when port isn't specified.
Signed-off-by: Bharata B Rao <bharata@linux.vnet.ibm.com>
---
qemu-sockets.c | 43 ++++++++++++++++++++++++++++++++++---------
1 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/qemu-sockets.c b/qemu-sockets.c
index b292311..25285f2 100644
--- a/qemu-sockets.c
+++ b/qemu-sockets.c
@@ -406,26 +406,36 @@ err:
return -1;
}
-/* compatibility wrapper */
-static int inet_parse(QemuOpts *opts, const char *str)
+/*
+ * Converts inet address and port specification in @str to QemuOpts
+ * options.
+ *
+ * If port isn't specified in @str, @default_port will be set in the
+ * port option of @opts. If default_port is -1, then the parser will
+ * look for the port specification in @str mandatorily.
+ */
+static int inet_parse(QemuOpts *opts, const char *str, int default_port)
{
const char *optstr, *h;
char addr[64];
char port[33];
- int pos;
+ int ret, pos, addr_pos = -1, port_pos = -1;
/* parse address */
if (str[0] == ':') {
/* no host given */
addr[0] = '\0';
- if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
+ ret = sscanf(str, ":%32[^,]%n", port, &port_pos);
+ if (port_pos == -1 || ret == EOF) {
fprintf(stderr, "%s: portonly parse error (%s)\n",
__FUNCTION__, str);
return -1;
}
} else if (str[0] == '[') {
/* IPv6 addr */
- if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
+ ret = sscanf(str, "[%64[^]]]%n:%32[^,]%n", addr, &addr_pos,
+ port, &port_pos);
+ if (addr_pos == -1 || ret == EOF) {
fprintf(stderr, "%s: ipv6 parse error (%s)\n",
__FUNCTION__, str);
return -1;
@@ -433,7 +443,9 @@ static int inet_parse(QemuOpts *opts, const char *str)
qemu_opt_set(opts, "ipv6", "on");
} else if (qemu_isdigit(str[0])) {
/* IPv4 addr */
- if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
+ ret = sscanf(str, "%64[0-9.]%n:%32[^,]%n", addr, &addr_pos,
+ port, &port_pos);
+ if (addr_pos == -1 || ret == EOF) {
fprintf(stderr, "%s: ipv4 parse error (%s)\n",
__FUNCTION__, str);
return -1;
@@ -441,12 +453,25 @@ static int inet_parse(QemuOpts *opts, const char *str)
qemu_opt_set(opts, "ipv4", "on");
} else {
/* hostname */
- if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
+ ret = sscanf(str, "%64[^:]%n:%32[^,]%n", addr, &addr_pos,
+ port, &port_pos);
+ if (addr_pos == -1 || ret == EOF) {
fprintf(stderr, "%s: hostname parse error (%s)\n",
__FUNCTION__, str);
return -1;
}
}
+
+ if (port_pos != -1) {
+ pos = port_pos;
+ } else {
+ pos = addr_pos;
+ if (default_port == -1) {
+ return -1;
+ }
+ snprintf(port, 33, "%d", default_port);
+ }
+
qemu_opt_set(opts, "host", addr);
qemu_opt_set(opts, "port", port);
@@ -470,7 +495,7 @@ int inet_listen(const char *str, char *ostr, int olen,
int sock = -1;
opts = qemu_opts_create(&socket_opts, NULL, 0, NULL);
- if (inet_parse(opts, str) == 0) {
+ if (inet_parse(opts, str, -1) == 0) {
sock = inet_listen_opts(opts, port_offset, errp);
if (sock != -1 && ostr) {
optstr = strchr(str, ',');
@@ -499,7 +524,7 @@ int inet_connect(const char *str, bool block, bool *in_progress, Error **errp)
int sock = -1;
opts = qemu_opts_create(&socket_opts, NULL, 0, NULL);
- if (inet_parse(opts, str) == 0) {
+ if (inet_parse(opts, str, -1) == 0) {
if (block) {
qemu_opt_set(opts, "block", "on");
}
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH v8 3/6] sockets: Make inet_parse() non static.
2012-09-24 8:51 [Qemu-devel] [PATCH v8 0/6] GlusterFS support in QEMU - v8 Bharata B Rao
2012-09-24 8:52 ` [Qemu-devel] [PATCH v8 1/6] sockets: publish dummy_opts Bharata B Rao
2012-09-24 8:53 ` [Qemu-devel] [PATCH v8 2/6] sockets: Make port specification optional in inet_parse Bharata B Rao
@ 2012-09-24 8:54 ` Bharata B Rao
2012-09-24 8:54 ` [Qemu-devel] [PATCH v8 4/6] aio: Fix qemu_aio_wait() to maintain correct walking_handlers count Bharata B Rao
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Bharata B Rao @ 2012-09-24 8:54 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Anthony Liguori, Anand Avati, Vijay Bellur,
Stefan Hajnoczi, Harsh Bora, Amar Tumballi, Markus Armbruster,
Blue Swirl, Avi Kivity, Paolo Bonzini
sockets: Make inet_parse() non static.
From: Bharata B Rao <bharata@linux.vnet.ibm.com>
Make inet_parse() non-static so that other subsystems like gluster
can use it to parse inet addresses.
Signed-off-by: Bharata B Rao <bharata@linux.vnet.ibm.com>
---
qemu-sockets.c | 2 +-
qemu_socket.h | 1 +
2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/qemu-sockets.c b/qemu-sockets.c
index 25285f2..e01ea8a 100644
--- a/qemu-sockets.c
+++ b/qemu-sockets.c
@@ -414,7 +414,7 @@ err:
* port option of @opts. If default_port is -1, then the parser will
* look for the port specification in @str mandatorily.
*/
-static int inet_parse(QemuOpts *opts, const char *str, int default_port)
+int inet_parse(QemuOpts *opts, const char *str, int default_port)
{
const char *optstr, *h;
char addr[64];
diff --git a/qemu_socket.h b/qemu_socket.h
index c87ee57..309a389 100644
--- a/qemu_socket.h
+++ b/qemu_socket.h
@@ -48,6 +48,7 @@ int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp);
int inet_connect(const char *str, bool block, bool *in_progress, Error **errp);
int inet_dgram_opts(QemuOpts *opts);
const char *inet_strfamily(int family);
+int inet_parse(QemuOpts *opts, const char *str, int default_port);
int unix_listen_opts(QemuOpts *opts);
int unix_listen(const char *path, char *ostr, int olen);
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH v8 4/6] aio: Fix qemu_aio_wait() to maintain correct walking_handlers count
2012-09-24 8:51 [Qemu-devel] [PATCH v8 0/6] GlusterFS support in QEMU - v8 Bharata B Rao
` (2 preceding siblings ...)
2012-09-24 8:54 ` [Qemu-devel] [PATCH v8 3/6] sockets: Make inet_parse() non static Bharata B Rao
@ 2012-09-24 8:54 ` Bharata B Rao
2012-09-24 8:55 ` [Qemu-devel] [PATCH v8 5/6] configure: Add a config option for GlusterFS as block backend Bharata B Rao
2012-09-24 8:56 ` [Qemu-devel] [PATCH v8 6/6] block: Support GlusterFS as a QEMU " Bharata B Rao
5 siblings, 0 replies; 7+ messages in thread
From: Bharata B Rao @ 2012-09-24 8:54 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Anthony Liguori, Anand Avati, Vijay Bellur,
Stefan Hajnoczi, Harsh Bora, Amar Tumballi, Markus Armbruster,
Blue Swirl, Avi Kivity, Paolo Bonzini
aio: Fix qemu_aio_wait() to maintain correct walking_handlers count
From: Paolo Bonzini <pbonzini@redhat.com>
Fix qemu_aio_wait() to ensure that registered aio handlers don't get
deleted when they are still active. This is ensured by maintaning the
right count of walking_handlers.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Bharata B Rao <bharata@linux.vnet.ibm.com>
---
aio.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/aio.c b/aio.c
index 0a9eb10..99b8b72 100644
--- a/aio.c
+++ b/aio.c
@@ -119,7 +119,7 @@ bool qemu_aio_wait(void)
return true;
}
- walking_handlers = 1;
+ walking_handlers++;
FD_ZERO(&rdfds);
FD_ZERO(&wrfds);
@@ -147,7 +147,7 @@ bool qemu_aio_wait(void)
}
}
- walking_handlers = 0;
+ walking_handlers--;
/* No AIO operations? Get us out of here */
if (!busy) {
@@ -159,7 +159,7 @@ bool qemu_aio_wait(void)
/* if we have any readable fds, dispatch event */
if (ret > 0) {
- walking_handlers = 1;
+ walking_handlers++;
/* we have to walk very carefully in case
* qemu_aio_set_fd_handler is called while we're walking */
@@ -187,7 +187,7 @@ bool qemu_aio_wait(void)
}
}
- walking_handlers = 0;
+ walking_handlers--;
}
return true;
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH v8 5/6] configure: Add a config option for GlusterFS as block backend
2012-09-24 8:51 [Qemu-devel] [PATCH v8 0/6] GlusterFS support in QEMU - v8 Bharata B Rao
` (3 preceding siblings ...)
2012-09-24 8:54 ` [Qemu-devel] [PATCH v8 4/6] aio: Fix qemu_aio_wait() to maintain correct walking_handlers count Bharata B Rao
@ 2012-09-24 8:55 ` Bharata B Rao
2012-09-24 8:56 ` [Qemu-devel] [PATCH v8 6/6] block: Support GlusterFS as a QEMU " Bharata B Rao
5 siblings, 0 replies; 7+ messages in thread
From: Bharata B Rao @ 2012-09-24 8:55 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Anthony Liguori, Anand Avati, Vijay Bellur,
Stefan Hajnoczi, Harsh Bora, Amar Tumballi, Markus Armbruster,
Blue Swirl, Avi Kivity, Paolo Bonzini
configure: Add a config option for GlusterFS as block backend
From: Bharata B Rao <bharata@linux.vnet.ibm.com>
GlusterFS support in QEMU depends on libgfapi, libgfrpc and
libgfxdr provided by GlusterFS.
Signed-off-by: Bharata B Rao <bharata@linux.vnet.ibm.com>
---
configure | 35 +++++++++++++++++++++++++++++++++++
1 files changed, 35 insertions(+), 0 deletions(-)
diff --git a/configure b/configure
index 1b86517..eb1af9f 100755
--- a/configure
+++ b/configure
@@ -219,6 +219,7 @@ want_tools="yes"
libiscsi=""
coroutine=""
seccomp=""
+glusterfs=""
# parse CC options first
for opt do
@@ -856,6 +857,10 @@ for opt do
;;
--disable-seccomp) seccomp="no"
;;
+ --disable-glusterfs) glusterfs="no"
+ ;;
+ --enable-glusterfs) glusterfs="yes"
+ ;;
*) echo "ERROR: unknown option $opt"; show_help="yes"
;;
esac
@@ -1128,6 +1133,8 @@ echo " --disable-seccomp disable seccomp support"
echo " --enable-seccomp enables seccomp support"
echo " --with-coroutine=BACKEND coroutine backend. Supported options:"
echo " gthread, ucontext, sigaltstack, windows"
+echo " --enable-glusterfs enable GlusterFS backend"
+echo " --disable-glusterfs disable GlusterFS backend"
echo ""
echo "NOTE: The object files are built at the place where configure is launched"
exit 1
@@ -2307,6 +2314,29 @@ EOF
fi
fi
+##########################################
+# glusterfs probe
+if test "$glusterfs" != "no" ; then
+ cat > $TMPC <<EOF
+#include <glusterfs/api/glfs.h>
+int main(void) {
+ (void) glfs_new("volume");
+ return 0;
+}
+EOF
+ glusterfs_libs="-lgfapi -lgfrpc -lgfxdr"
+ if compile_prog "" "$glusterfs_libs" ; then
+ glusterfs=yes
+ libs_tools="$glusterfs_libs $libs_tools"
+ libs_softmmu="$glusterfs_libs $libs_softmmu"
+ else
+ if test "$glusterfs" = "yes" ; then
+ feature_not_found "GlusterFS backend support"
+ fi
+ glusterfs=no
+ fi
+fi
+
#
# Check for xxxat() functions when we are building linux-user
# emulator. This is done because older glibc versions don't
@@ -3174,6 +3204,7 @@ echo "libiscsi support $libiscsi"
echo "build guest agent $guest_agent"
echo "seccomp support $seccomp"
echo "coroutine backend $coroutine_backend"
+echo "GlusterFS support $glusterfs"
if test "$sdl_too_old" = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3520,6 +3551,10 @@ if test "$has_environ" = "yes" ; then
echo "CONFIG_HAS_ENVIRON=y" >> $config_host_mak
fi
+if test "$glusterfs" = "yes" ; then
+ echo "CONFIG_GLUSTERFS=y" >> $config_host_mak
+fi
+
# USB host support
case "$usb" in
linux)
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH v8 6/6] block: Support GlusterFS as a QEMU block backend.
2012-09-24 8:51 [Qemu-devel] [PATCH v8 0/6] GlusterFS support in QEMU - v8 Bharata B Rao
` (4 preceding siblings ...)
2012-09-24 8:55 ` [Qemu-devel] [PATCH v8 5/6] configure: Add a config option for GlusterFS as block backend Bharata B Rao
@ 2012-09-24 8:56 ` Bharata B Rao
5 siblings, 0 replies; 7+ messages in thread
From: Bharata B Rao @ 2012-09-24 8:56 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Anthony Liguori, Anand Avati, Vijay Bellur,
Stefan Hajnoczi, Harsh Bora, Amar Tumballi, Markus Armbruster,
Blue Swirl, Avi Kivity, Paolo Bonzini
block: Support GlusterFS as a QEMU block backend.
From: Bharata B Rao <bharata@linux.vnet.ibm.com>
This patch adds gluster as the new block backend in QEMU. This gives
QEMU the ability to boot VM images from gluster volumes. Its already
possible to boot from VM images on gluster volumes using FUSE mount, but
this patchset provides the ability to boot VM images from gluster volumes
by by-passing the FUSE layer in gluster. This is made possible by
using libgfapi routines to perform IO on gluster volumes directly.
VM Image on gluster volume is specified like this:
file=gluster[+transport]://[server[:port]]/volname/image[?socket=...]
'gluster' is the protocol.
'transport' specifies the transport type used to connect to gluster
management daemon (glusterd). Valid transport types are
tcp, unix and rdma. If a transport type isn't specified, then tcp
type is assumed.
'server' specifies the server where the volume file specification for
the given volume resides. This can be either hostname, ipv4 address
or ipv6 address. ipv6 address needs to be within square brackets [ ].
If transport type is 'unix', then server field is ignored, but the
'socket' field needs to be populated with the path to unix domain
socket.
'port' is the port number on which glusterd is listening. This is optional
and if not specified, QEMU will send 0 which will make gluster to use the
default port. port is ignored for unix type of transport.
'volname' is the name of the gluster volume which contains the VM image.
'image' is the path to the actual VM image that resides on gluster volume.
Examples:
file=gluster://1.2.3.4/testvol/a.img
file=gluster+tcp://1.2.3.4/testvol/a.img
file=gluster+tcp://1.2.3.4:24007/testvol/dir/a.img
file=gluster+tcp://[1:2:3:4:5:6:7:8]/testvol/dir/a.img
file=gluster+tcp://[1:2:3:4:5:6:7:8]:24007/testvol/dir/a.img
file=gluster+tcp://server.domain.com:24007/testvol/dir/a.img
file=gluster+unix:///testvol/dir/a.img?socket=/tmp/glusterd.socket
file=gluster+rdma://1.2.3.4:24007/testvol/a.img
Signed-off-by: Bharata B Rao <bharata@linux.vnet.ibm.com>
---
block/Makefile.objs | 1
block/gluster.c | 675 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 676 insertions(+), 0 deletions(-)
create mode 100644 block/gluster.c
diff --git a/block/Makefile.objs b/block/Makefile.objs
index b5754d3..a1ae67f 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -9,3 +9,4 @@ block-obj-$(CONFIG_POSIX) += raw-posix.o
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
block-obj-$(CONFIG_CURL) += curl.o
block-obj-$(CONFIG_RBD) += rbd.o
+block-obj-$(CONFIG_GLUSTERFS) += gluster.o
diff --git a/block/gluster.c b/block/gluster.c
new file mode 100644
index 0000000..d805791
--- /dev/null
+++ b/block/gluster.c
@@ -0,0 +1,675 @@
+/*
+ * GlusterFS backend for QEMU
+ *
+ * Copyright (C) 2012 Bharata B Rao <bharata@linux.vnet.ibm.com>
+ *
+ * Pipe handling mechanism in AIO implementation is derived from
+ * block/rbd.c. Hence,
+ *
+ * Copyright (C) 2010-2011 Christian Brunner <chb@muc.de>,
+ * Josh Durgin <josh.durgin@dreamhost.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+#include <glusterfs/api/glfs.h>
+#include "block_int.h"
+#include "qemu_socket.h"
+
+typedef struct GlusterAIOCB {
+ BlockDriverAIOCB common;
+ int64_t size;
+ int ret;
+ bool *finished;
+ QEMUBH *bh;
+} GlusterAIOCB;
+
+typedef struct BDRVGlusterState {
+ struct glfs *glfs;
+ int fds[2];
+ struct glfs_fd *fd;
+ int qemu_aio_count;
+ int event_reader_pos;
+ GlusterAIOCB *event_acb;
+} BDRVGlusterState;
+
+#define GLUSTER_FD_READ 0
+#define GLUSTER_FD_WRITE 1
+
+typedef struct GlusterURI {
+ char *server;
+ int port;
+ char *volname;
+ char *image;
+ char *transport;
+ bool is_unix;
+} GlusterURI;
+
+static void qemu_gluster_uri_free(GlusterURI *uri)
+{
+ g_free(uri->server);
+ g_free(uri->volname);
+ g_free(uri->image);
+ g_free(uri->transport);
+ g_free(uri);
+}
+
+static int parse_query_options(GlusterURI *uri, char *options)
+{
+ char *token, *saveptr;
+
+ if (!options) {
+ return 0;
+ }
+ token = strtok_r(options, "=", &saveptr);
+ if (!token || strcmp(token, "socket")) {
+ return -EINVAL;
+ }
+ token = strtok_r(NULL, "&", &saveptr);
+ if (!token) {
+ return -EINVAL;
+ }
+ uri->server = g_strdup(token);
+ if (!uri->is_unix) {
+ return -EINVAL;
+ }
+
+ /* Flag error for extra options */
+ token = strtok_r(NULL, "&", &saveptr);
+ if (token) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int parse_gluster_spec(GlusterURI *uri, char *spec)
+{
+ char *token, *saveptr;
+ int ret;
+ QemuOpts *opts;
+ char *p, *q;
+
+ /* transport */
+ p = spec;
+ if (strstart(p, "gluster://", (const char **)&p)) {
+ uri->transport = g_strdup("tcp");
+ } else if (strstart(p, "gluster+tcp://", (const char **)&p)) {
+ uri->transport = g_strdup("tcp");
+ } else if (strstart(p, "gluster+unix://", (const char **)&p)) {
+ uri->transport = g_strdup("unix");
+ uri->is_unix = true;
+ } else if (strstart(p, "gluster+rdma://", (const char **)&p)) {
+ uri->transport = g_strdup("rdma");
+ } else {
+ return -EINVAL;
+ }
+ q = p;
+
+ /* server */
+ if (strcmp(uri->transport, "unix")) {
+ p = strchr(p, '/');
+ if (!p) {
+ return -EINVAL;
+ }
+ *p++ = '\0';
+ opts = qemu_opts_create(&socket_opts, NULL, 0, NULL);
+ ret = inet_parse(opts, q, 0);
+ if (!ret) {
+ uri->server = g_strdup(qemu_opt_get(opts, "host"));
+ uri->port = strtoul(qemu_opt_get(opts, "port"), NULL, 0);
+ if (uri->port < 0) {
+ ret = -EINVAL;
+ }
+ }
+ qemu_opts_del(opts);
+ if (ret < 0) {
+ return -EINVAL;
+ }
+ }
+
+ /* volname */
+ token = strtok_r(p, "/", &saveptr);
+ if (!token) {
+ return -EINVAL;
+ }
+ uri->volname = g_strdup(token);
+
+ /* image */
+ token = strtok_r(NULL, "?", &saveptr);
+ if (!token) {
+ return -EINVAL;
+ }
+ uri->image = g_strdup(token);
+ return 0;
+}
+
+/*
+ * file=gluster[+transport]://[server[:port]]/volname/image[?socket=...]
+ *
+ * 'gluster' is the protocol.
+ *
+ * 'transport' specifies the transport type used to connect to gluster
+ * management daemon (glusterd). Valid transport types are
+ * tcp, unix and rdma. If a transport type isn't specified, then tcp
+ * type is assumed.
+ *
+ * 'server' specifies the server where the volume file specification for
+ * the given volume resides. This can be either hostname, ipv4 address
+ * or ipv6 address. ipv6 address needs to be within square brackets [ ].
+ * If transport type is 'unix', then server field is ignored, but the
+ * 'socket' field needs to be populated with the path to unix domain
+ * socket.
+ *
+ * 'port' is the port number on which glusterd is listening. This is optional
+ * and if not specified, QEMU will send 0 which will make gluster to use the
+ * default port. port is ignored for unix type of transport.
+ *
+ * 'volname' is the name of the gluster volume which contains the VM image.
+ *
+ * 'image' is the path to the actual VM image that resides on gluster volume.
+ *
+ * Examples:
+ *
+ * file=gluster://1.2.3.4/testvol/a.img
+ * file=gluster+tcp://1.2.3.4/testvol/a.img
+ * file=gluster+tcp://1.2.3.4:24007/testvol/dir/a.img
+ * file=gluster+tcp://[1:2:3:4:5:6:7:8]/testvol/dir/a.img
+ * file=gluster+tcp://[1:2:3:4:5:6:7:8]:24007/testvol/dir/a.img
+ * file=gluster+tcp://server.domain.com:24007/testvol/dir/a.img
+ * file=gluster+unix:///testvol/dir/a.img?socket=/tmp/glusterd.socket
+ * file=gluster+rdma://1.2.3.4:24007/testvol/a.img
+ */
+static int qemu_gluster_parseuri(GlusterURI *uri, const char *filename)
+{
+ char *token, *saveptr;
+ char *p, *gluster_spec = NULL;
+ int ret = -EINVAL;
+
+ p = g_strdup(filename);
+
+ /* Extract server, volname and image */
+ token = strtok_r(p, "?", &saveptr);
+ if (!token) {
+ goto out;
+ }
+ gluster_spec = g_strdup(token);
+ ret = parse_gluster_spec(uri, gluster_spec);
+ if (ret < 0) {
+ goto out;
+ }
+
+ /* socket */
+ token = strtok_r(NULL, "?", &saveptr);
+ ret = parse_query_options(uri, token);
+ if (ret < 0) {
+ goto out;
+ }
+ ret = 0;
+out:
+ g_free(p);
+ g_free(gluster_spec);
+ return ret;
+}
+
+static struct glfs *qemu_gluster_init(GlusterURI *uri, const char *filename)
+{
+ struct glfs *glfs = NULL;
+ int ret;
+
+ ret = qemu_gluster_parseuri(uri, filename);
+ if (ret < 0) {
+ error_report("Usage: file=gluster[+transport]://[server[:port]]/"
+ "volname/image[?socket=...]");
+ errno = -ret;
+ goto out;
+ }
+
+ glfs = glfs_new(uri->volname);
+ if (!glfs) {
+ goto out;
+ }
+
+ ret = glfs_set_volfile_server(glfs, uri->transport, uri->server, uri->port);
+ if (ret < 0) {
+ goto out;
+ }
+
+ /*
+ * TODO: Use GF_LOG_ERROR instead of hard code value of 4 here when
+ * GlusterFS makes GF_LOG_* macros available to libgfapi users.
+ */
+ ret = glfs_set_logging(glfs, "-", 4);
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = glfs_init(glfs);
+ if (ret) {
+ error_report("Gluster connection failed for server=%s port=%d "
+ "volume=%s image=%s transport=%s\n", uri->server, uri->port,
+ uri->volname, uri->image, uri->transport);
+ goto out;
+ }
+ return glfs;
+
+out:
+ if (glfs) {
+ glfs_fini(glfs);
+ }
+ return NULL;
+}
+
+static void qemu_gluster_complete_aio(GlusterAIOCB *acb, BDRVGlusterState *s)
+{
+ int ret;
+ bool *finished = acb->finished;
+ BlockDriverCompletionFunc *cb = acb->common.cb;
+ void *opaque = acb->common.opaque;
+
+ if (!acb->ret || acb->ret == acb->size) {
+ ret = 0; /* Success */
+ } else if (acb->ret < 0) {
+ ret = acb->ret; /* Read/Write failed */
+ } else {
+ ret = -EIO; /* Partial read/write - fail it */
+ }
+
+ s->qemu_aio_count--;
+ qemu_aio_release(acb);
+ cb(opaque, ret);
+ if (finished) {
+ *finished = true;
+ }
+}
+
+static void qemu_gluster_aio_event_reader(void *opaque)
+{
+ BDRVGlusterState *s = opaque;
+ ssize_t ret;
+
+ do {
+ char *p = (char *)&s->event_acb;
+
+ ret = read(s->fds[GLUSTER_FD_READ], p + s->event_reader_pos,
+ sizeof(s->event_acb) - s->event_reader_pos);
+ if (ret > 0) {
+ s->event_reader_pos += ret;
+ if (s->event_reader_pos == sizeof(s->event_acb)) {
+ s->event_reader_pos = 0;
+ qemu_gluster_complete_aio(s->event_acb, s);
+ }
+ }
+ } while (ret < 0 && errno == EINTR);
+}
+
+static int qemu_gluster_aio_flush_cb(void *opaque)
+{
+ BDRVGlusterState *s = opaque;
+
+ return (s->qemu_aio_count > 0);
+}
+
+static int qemu_gluster_open(BlockDriverState *bs, const char *filename,
+ int bdrv_flags)
+{
+ BDRVGlusterState *s = bs->opaque;
+ int open_flags = 0;
+ int ret = 0;
+ GlusterURI *uri = g_malloc0(sizeof(GlusterURI));
+
+ s->glfs = qemu_gluster_init(uri, filename);
+ if (!s->glfs) {
+ ret = -errno;
+ goto out;
+ }
+
+ open_flags |= O_BINARY;
+ open_flags &= ~O_ACCMODE;
+ if (bdrv_flags & BDRV_O_RDWR) {
+ open_flags |= O_RDWR;
+ } else {
+ open_flags |= O_RDONLY;
+ }
+
+ if ((bdrv_flags & BDRV_O_NOCACHE)) {
+ open_flags |= O_DIRECT;
+ }
+
+ s->fd = glfs_open(s->glfs, uri->image, open_flags);
+ if (!s->fd) {
+ ret = -errno;
+ goto out;
+ }
+
+ ret = qemu_pipe(s->fds);
+ if (ret < 0) {
+ ret = -errno;
+ goto out;
+ }
+ fcntl(s->fds[GLUSTER_FD_READ], F_SETFL, O_NONBLOCK);
+ qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ],
+ qemu_gluster_aio_event_reader, NULL, qemu_gluster_aio_flush_cb, s);
+
+out:
+ qemu_gluster_uri_free(uri);
+ if (!ret) {
+ return ret;
+ }
+ if (s->fd) {
+ glfs_close(s->fd);
+ }
+ if (s->glfs) {
+ glfs_fini(s->glfs);
+ }
+ return ret;
+}
+
+static int qemu_gluster_create(const char *filename,
+ QEMUOptionParameter *options)
+{
+ struct glfs *glfs;
+ struct glfs_fd *fd;
+ int ret = 0;
+ int64_t total_size = 0;
+ GlusterURI *uri = g_malloc0(sizeof(GlusterURI));
+
+ glfs = qemu_gluster_init(uri, filename);
+ if (!glfs) {
+ ret = -errno;
+ goto out;
+ }
+
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ total_size = options->value.n / BDRV_SECTOR_SIZE;
+ }
+ options++;
+ }
+
+ fd = glfs_creat(glfs, uri->image,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
+ if (!fd) {
+ ret = -errno;
+ } else {
+ if (glfs_ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) {
+ ret = -errno;
+ }
+ if (glfs_close(fd) != 0) {
+ ret = -errno;
+ }
+ }
+out:
+ qemu_gluster_uri_free(uri);
+ if (glfs) {
+ glfs_fini(glfs);
+ }
+ return ret;
+}
+
+static void qemu_gluster_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ GlusterAIOCB *acb = (GlusterAIOCB *)blockacb;
+ bool finished = false;
+
+ acb->finished = &finished;
+ while (!finished) {
+ qemu_aio_wait();
+ }
+}
+
+static AIOPool gluster_aio_pool = {
+ .aiocb_size = sizeof(GlusterAIOCB),
+ .cancel = qemu_gluster_aio_cancel,
+};
+
+static int qemu_gluster_send_pipe(BDRVGlusterState *s, GlusterAIOCB *acb)
+{
+ int ret = 0;
+
+ while (1) {
+ int fd = s->fds[GLUSTER_FD_WRITE];
+
+ ret = write(fd, (void *)&acb, sizeof(acb));
+ if (ret >= 0) {
+ break;
+ }
+ if (errno == EINTR) {
+ continue;
+ }
+ if (errno != EAGAIN) {
+ break;
+ }
+ }
+ return ret;
+}
+
+static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
+{
+ GlusterAIOCB *acb = (GlusterAIOCB *)arg;
+ BlockDriverState *bs = acb->common.bs;
+ BDRVGlusterState *s = bs->opaque;
+
+ acb->ret = ret;
+ if (qemu_gluster_send_pipe(s, acb) < 0) {
+ /*
+ * Gluster AIO callback thread failed to notify the waiting
+ * QEMU thread about IO completion.
+ *
+ * Complete this IO request and make the disk inaccessible for
+ * subsequent reads and writes.
+ */
+ error_report("Gluster failed to notify QEMU about IO completion");
+
+ qemu_mutex_lock_iothread(); /* We are in gluster thread context */
+ acb->common.cb(acb->common.opaque, -EIO);
+ qemu_aio_release(acb);
+ s->qemu_aio_count--;
+ close(s->fds[GLUSTER_FD_READ]);
+ close(s->fds[GLUSTER_FD_WRITE]);
+ qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ], NULL, NULL, NULL,
+ NULL);
+ bs->drv = NULL; /* Make the disk inaccessible */
+ qemu_mutex_unlock_iothread();
+ }
+}
+
+static BlockDriverAIOCB *qemu_gluster_aio_rw(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque, int write)
+{
+ int ret;
+ GlusterAIOCB *acb;
+ BDRVGlusterState *s = bs->opaque;
+ size_t size;
+ off_t offset;
+
+ offset = sector_num * BDRV_SECTOR_SIZE;
+ size = nb_sectors * BDRV_SECTOR_SIZE;
+ s->qemu_aio_count++;
+
+ acb = qemu_aio_get(&gluster_aio_pool, bs, cb, opaque);
+ acb->size = size;
+ acb->ret = 0;
+ acb->finished = NULL;
+
+ if (write) {
+ ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
+ &gluster_finish_aiocb, acb);
+ } else {
+ ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0,
+ &gluster_finish_aiocb, acb);
+ }
+
+ if (ret < 0) {
+ goto out;
+ }
+ return &acb->common;
+
+out:
+ s->qemu_aio_count--;
+ qemu_aio_release(acb);
+ return NULL;
+}
+
+static BlockDriverAIOCB *qemu_gluster_aio_readv(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return qemu_gluster_aio_rw(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
+}
+
+static BlockDriverAIOCB *qemu_gluster_aio_writev(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return qemu_gluster_aio_rw(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
+}
+
+static BlockDriverAIOCB *qemu_gluster_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ int ret;
+ GlusterAIOCB *acb;
+ BDRVGlusterState *s = bs->opaque;
+
+ acb = qemu_aio_get(&gluster_aio_pool, bs, cb, opaque);
+ acb->size = 0;
+ acb->ret = 0;
+ acb->finished = NULL;
+ s->qemu_aio_count++;
+
+ ret = glfs_fsync_async(s->fd, &gluster_finish_aiocb, acb);
+ if (ret < 0) {
+ goto out;
+ }
+ return &acb->common;
+
+out:
+ s->qemu_aio_count--;
+ qemu_aio_release(acb);
+ return NULL;
+}
+
+static int64_t qemu_gluster_getlength(BlockDriverState *bs)
+{
+ BDRVGlusterState *s = bs->opaque;
+ int64_t ret;
+
+ ret = glfs_lseek(s->fd, 0, SEEK_END);
+ if (ret < 0) {
+ return -errno;
+ } else {
+ return ret;
+ }
+}
+
+static int64_t qemu_gluster_allocated_file_size(BlockDriverState *bs)
+{
+ BDRVGlusterState *s = bs->opaque;
+ struct stat st;
+ int ret;
+
+ ret = glfs_fstat(s->fd, &st);
+ if (ret < 0) {
+ return -errno;
+ } else {
+ return st.st_blocks * 512;
+ }
+}
+
+static void qemu_gluster_close(BlockDriverState *bs)
+{
+ BDRVGlusterState *s = bs->opaque;
+
+ close(s->fds[GLUSTER_FD_READ]);
+ close(s->fds[GLUSTER_FD_WRITE]);
+ qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ], NULL, NULL, NULL, NULL);
+
+ if (s->fd) {
+ glfs_close(s->fd);
+ s->fd = NULL;
+ }
+ glfs_fini(s->glfs);
+}
+
+static QEMUOptionParameter qemu_gluster_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ { NULL }
+};
+
+static BlockDriver bdrv_gluster = {
+ .format_name = "gluster",
+ .protocol_name = "gluster",
+ .instance_size = sizeof(BDRVGlusterState),
+ .bdrv_file_open = qemu_gluster_open,
+ .bdrv_close = qemu_gluster_close,
+ .bdrv_create = qemu_gluster_create,
+ .bdrv_getlength = qemu_gluster_getlength,
+ .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
+ .bdrv_aio_readv = qemu_gluster_aio_readv,
+ .bdrv_aio_writev = qemu_gluster_aio_writev,
+ .bdrv_aio_flush = qemu_gluster_aio_flush,
+ .create_options = qemu_gluster_create_options,
+};
+
+static BlockDriver bdrv_gluster_tcp = {
+ .format_name = "gluster",
+ .protocol_name = "gluster+tcp",
+ .instance_size = sizeof(BDRVGlusterState),
+ .bdrv_file_open = qemu_gluster_open,
+ .bdrv_close = qemu_gluster_close,
+ .bdrv_create = qemu_gluster_create,
+ .bdrv_getlength = qemu_gluster_getlength,
+ .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
+ .bdrv_aio_readv = qemu_gluster_aio_readv,
+ .bdrv_aio_writev = qemu_gluster_aio_writev,
+ .bdrv_aio_flush = qemu_gluster_aio_flush,
+ .create_options = qemu_gluster_create_options,
+};
+
+static BlockDriver bdrv_gluster_unix = {
+ .format_name = "gluster",
+ .protocol_name = "gluster+unix",
+ .instance_size = sizeof(BDRVGlusterState),
+ .bdrv_file_open = qemu_gluster_open,
+ .bdrv_close = qemu_gluster_close,
+ .bdrv_create = qemu_gluster_create,
+ .bdrv_getlength = qemu_gluster_getlength,
+ .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
+ .bdrv_aio_readv = qemu_gluster_aio_readv,
+ .bdrv_aio_writev = qemu_gluster_aio_writev,
+ .bdrv_aio_flush = qemu_gluster_aio_flush,
+ .create_options = qemu_gluster_create_options,
+};
+
+static BlockDriver bdrv_gluster_rdma = {
+ .format_name = "gluster",
+ .protocol_name = "gluster+rdma",
+ .instance_size = sizeof(BDRVGlusterState),
+ .bdrv_file_open = qemu_gluster_open,
+ .bdrv_close = qemu_gluster_close,
+ .bdrv_create = qemu_gluster_create,
+ .bdrv_getlength = qemu_gluster_getlength,
+ .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
+ .bdrv_aio_readv = qemu_gluster_aio_readv,
+ .bdrv_aio_writev = qemu_gluster_aio_writev,
+ .bdrv_aio_flush = qemu_gluster_aio_flush,
+ .create_options = qemu_gluster_create_options,
+};
+
+static void bdrv_gluster_init(void)
+{
+ bdrv_register(&bdrv_gluster_rdma);
+ bdrv_register(&bdrv_gluster_unix);
+ bdrv_register(&bdrv_gluster_tcp);
+ bdrv_register(&bdrv_gluster);
+}
+
+block_init(bdrv_gluster_init);
^ permalink raw reply related [flat|nested] 7+ messages in thread