On Tue, Aug 7, 2012 at 9:01 AM, Bharata B Rao <[email protected]> wrote: > block: Support GlusterFS as a QEMU block backend. > > From: Bharata B Rao <[email protected]> > > 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://server:[port]/volname/image[?transport=socket] > > 'gluster' is the protocol. > > 'server' specifies the server where the volume file specification for > the given volume resides. This can be either hostname or ipv4 address > or ipv6 address. ipv6 address needs to be with in square brackets [ ]. > > port' is the port number on which gluster management daemon (glusterd) is > listening. This is optional and if not specified, QEMU will send 0 which > will make libgfapi to use the default port. > > 'volname' is the name of the gluster volume which contains the VM image. > > 'image' is the path to the actual VM image in the gluster volume. > > 'transport' specifies the transport used to connect to glusterd. This is > optional and if not specified, socket transport is used. > > Examples: > > file=gluster://1.2.3.4/testvol/a.img > file=gluster://1.2.3.4:5000/testvol/dir/a.img?transport=socket > file=gluster://[1:2:3:4:5:6:7:8]/testvol/dir/a.img > file=gluster://[1:2:3:4:5:6:7:8]:5000/testvol/dir/a.img?transport=socket > file=gluster://server.domain.com:5000/testvol/dir/a.img > > Signed-off-by: Bharata B Rao <[email protected]> > Reviewed-by: Stefan Hajnoczi <[email protected]> > --- > > block/Makefile.objs | 1 > block/gluster.c | 623 > +++++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 624 insertions(+), 0 deletions(-) > create mode 100644 block/gluster.c
I have left a few small comments. Perhaps you can resend with your fixes to Patch 1? > 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..39c55fe > --- /dev/null > +++ b/block/gluster.c > @@ -0,0 +1,623 @@ > +/* > + * GlusterFS backend for QEMU > + * > + * (AIO implementation is derived from block/rbd.c) > + * > + * Copyright (C) 2012 Bharata B Rao <[email protected]> > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or > + * (at your option) any later version. See the COPYING file in the top-level > + * directory. > + */ > +#include "block_int.h" > +#include <glusterfs/api/glfs.h> System headers followed by user headers is a good order to prevent application-specific macros from interfering with system headers: #include <glusterfs/api/glfs.h> #include "block_int.h" > + > +typedef struct GlusterAIOCB { > + BlockDriverAIOCB common; > + bool canceled; > + int64_t size; > + int ret; > +} GlusterAIOCB; > + > +typedef struct BDRVGlusterState { > + struct glfs *glfs; > + int fds[2]; > + struct glfs_fd *fd; > + int qemu_aio_count; > +} BDRVGlusterState; > + > +#define GLUSTER_FD_READ 0 > +#define GLUSTER_FD_WRITE 1 > + > +typedef struct GlusterURI { > + char *server; > + int port; > + char *volname; > + char *image; > + char *transport; > +} 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); > +} > + > +/* > + * We don't validate the transport option obtained here but > + * instead depend on gluster to flag an error. > + */ > +static int parse_transport(GlusterURI *uri, char *transport) > +{ > + char *token, *saveptr; > + int ret = -EINVAL; > + > + if (!transport) { > + uri->transport = g_strdup("socket"); > + ret = 0; > + goto out; > + } > + > + token = strtok_r(transport, "=", &saveptr); > + if (!token) { > + goto out; > + } > + if (strcmp(token, "transport")) { > + goto out; > + } > + token = strtok_r(NULL, "=", &saveptr); > + if (!token) { > + goto out; > + } > + uri->transport = g_strdup(token); > + ret = 0; > +out: > + return ret; > +} > + > +static int parse_server(GlusterURI *uri, char *server) > +{ > + int ret = -EINVAL; > + char *token, *saveptr; > + char *p, *q = server; > + > + p = strchr(server, '['); > + if (p) { > + /* [ipv6] */ > + if (p != server) { > + /* [ not in the beginning */ > + goto out; > + } > + q++; > + p = strrchr(p, ']'); > + if (!p) { > + /* No matching ] */ > + goto out; > + } > + *p++ = '\0'; > + uri->server = g_strdup(q); > + > + if (*p) { > + if (*p != ':') { > + /* [ipv6] followed by something other than : */ > + goto out; > + } > + uri->port = strtoul(++p, NULL, 0); > + if (uri->port < 0) { > + goto out; > + } > + } else { > + /* port not specified, use default */ > + uri->port = 0; > + } > + > + } else { > + /* ipv4 or hostname */ > + if (*server == ':') { > + /* port specified w/o a server */ > + goto out; > + } > + token = strtok_r(server, ":", &saveptr); > + if (!token) { > + goto out; > + } > + uri->server = g_strdup(token); > + token = strtok_r(NULL, ":", &saveptr); > + if (token) { > + uri->port = strtoul(token, NULL, 0); > + if (uri->port < 0) { > + goto out; > + } > + } else { > + uri->port = 0; > + } > + } > + ret = 0; > +out: > + return ret; > +} > + > +/* > + * file=gluster://server:[port]/volname/image[?transport=socket] > + * > + * 'gluster' is the protocol. > + * > + * 'server' specifies the server where the volume file specification for > + * the given volume resides. This can be either hostname or ipv4 address > + * or ipv6 address. ipv6 address needs to be with in square brackets [ ]. > + * > + *'port' is the port number on which gluster management daemon (glusterd) is Missing space: * 'port' > + * listening. This is optional and if not specified, QEMU will send 0 which > + * will make libgfapi to use the default port. > + * > + * 'volname' is the name of the gluster volume which contains the VM image. > + * > + * 'image' is the path to the actual VM image in the gluster volume. > + * > + * 'transport' specifies the transport used to connect to glusterd. This is > + * optional and if not specified, socket transport is used. > + * > + * Examples: > + * > + * file=gluster://1.2.3.4/testvol/a.img > + * file=gluster://1.2.3.4:5000/testvol/dir/a.img?transport=socket > + * file=gluster://[1:2:3:4:5:6:7:8]/testvol/dir/a.img > + * file=gluster://[1:2:3:4:5:6:7:8]:5000/testvol/dir/a.img?transport=socket > + * file=gluster://server.domain.com:5000/testvol/dir/a.img > + * > + * We just do minimal checking of the gluster options and mostly ensure > + * that all the expected elements of the URI are present. We depend on > libgfapi > + * APIs to return appropriate errors in case of invalid arguments. > + */ > +static int qemu_gluster_parseuri(GlusterURI *uri, const char *filename) > +{ > + char *token, *saveptr; > + char *p, *r; > + int ret = -EINVAL; > + > + p = r = g_strdup(filename); > + if (strncmp(p, "gluster://", 10)) { > + goto out; > + } > + > + /* Discard the protocol */ > + p += 10; > + > + /* server */ > + token = strtok_r(p, "/", &saveptr); > + if (!token) { > + goto out; > + } > + > + ret = parse_server(uri, token); > + if (ret < 0) { > + goto out; > + } > + > + /* volname */ > + token = strtok_r(NULL, "/", &saveptr); > + if (!token) { > + ret = -EINVAL; > + goto out; > + } > + uri->volname = g_strdup(token); > + > + /* image */ > + token = strtok_r(NULL, "?", &saveptr); > + if (!token) { > + ret = -EINVAL; > + goto out; > + } > + uri->image = g_strdup(token); > + > + /* transport */ > + token = strtok_r(NULL, "?", &saveptr); > + ret = parse_transport(uri, token); > + if (ret < 0) { > + goto out; > + } > + > + /* Flag error for extra options */ > + token = strtok_r(NULL, "?", &saveptr); > + if (token) { > + ret = -EINVAL; > + goto out; > + } > + ret = 0; > +out: > + g_free(r); > + 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://server:[port]/volname/image" server[:port] > + "[?transport=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 exports it in a header. > + */ > + ret = glfs_set_logging(glfs, "-", 4); Are you submitting the GlusterFS patch to move gf_loglevel_t/GF_LOG_ERROR to the API headers?
