[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v5 2/2] block: Support GlusterFS as a QEMU block
From: |
Stefan Hajnoczi |
Subject: |
Re: [Qemu-devel] [PATCH v5 2/2] block: Support GlusterFS as a QEMU block backend |
Date: |
Wed, 8 Aug 2012 15:37:31 +0100 |
On Tue, Aug 7, 2012 at 9:01 AM, Bharata B Rao
<address@hidden> wrote:
> block: Support GlusterFS as a QEMU block backend.
>
> From: Bharata B Rao <address@hidden>
>
> 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 <address@hidden>
> Reviewed-by: Stefan Hajnoczi <address@hidden>
> ---
>
> 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 <address@hidden>
> + *
> + * 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?