xref: /openbmc/qemu/block/gluster.c (revision b8bcf811)
1 /*
2  * GlusterFS backend for QEMU
3  *
4  * Copyright (C) 2012 Bharata B Rao <bharata@linux.vnet.ibm.com>
5  *
6  * Pipe handling mechanism in AIO implementation is derived from
7  * block/rbd.c. Hence,
8  *
9  * Copyright (C) 2010-2011 Christian Brunner <chb@muc.de>,
10  *                         Josh Durgin <josh.durgin@dreamhost.com>
11  *
12  * This work is licensed under the terms of the GNU GPL, version 2.  See
13  * the COPYING file in the top-level directory.
14  *
15  * Contributions after 2012-01-13 are licensed under the terms of the
16  * GNU GPL, version 2 or (at your option) any later version.
17  */
18 #include <glusterfs/api/glfs.h>
19 #include "block/block_int.h"
20 #include "qemu/sockets.h"
21 #include "qemu/uri.h"
22 
23 typedef struct GlusterAIOCB {
24     int64_t size;
25     int ret;
26     QEMUBH *bh;
27     Coroutine *coroutine;
28 } GlusterAIOCB;
29 
30 typedef struct BDRVGlusterState {
31     struct glfs *glfs;
32     struct glfs_fd *fd;
33 } BDRVGlusterState;
34 
35 #define GLUSTER_FD_READ  0
36 #define GLUSTER_FD_WRITE 1
37 
38 typedef struct GlusterConf {
39     char *server;
40     int port;
41     char *volname;
42     char *image;
43     char *transport;
44 } GlusterConf;
45 
46 static void qemu_gluster_gconf_free(GlusterConf *gconf)
47 {
48     g_free(gconf->server);
49     g_free(gconf->volname);
50     g_free(gconf->image);
51     g_free(gconf->transport);
52     g_free(gconf);
53 }
54 
55 static int parse_volume_options(GlusterConf *gconf, char *path)
56 {
57     char *p, *q;
58 
59     if (!path) {
60         return -EINVAL;
61     }
62 
63     /* volume */
64     p = q = path + strspn(path, "/");
65     p += strcspn(p, "/");
66     if (*p == '\0') {
67         return -EINVAL;
68     }
69     gconf->volname = g_strndup(q, p - q);
70 
71     /* image */
72     p += strspn(p, "/");
73     if (*p == '\0') {
74         return -EINVAL;
75     }
76     gconf->image = g_strdup(p);
77     return 0;
78 }
79 
80 /*
81  * file=gluster[+transport]://[server[:port]]/volname/image[?socket=...]
82  *
83  * 'gluster' is the protocol.
84  *
85  * 'transport' specifies the transport type used to connect to gluster
86  * management daemon (glusterd). Valid transport types are
87  * tcp, unix and rdma. If a transport type isn't specified, then tcp
88  * type is assumed.
89  *
90  * 'server' specifies the server where the volume file specification for
91  * the given volume resides. This can be either hostname, ipv4 address
92  * or ipv6 address. ipv6 address needs to be within square brackets [ ].
93  * If transport type is 'unix', then 'server' field should not be specifed.
94  * The 'socket' field needs to be populated with the path to unix domain
95  * socket.
96  *
97  * 'port' is the port number on which glusterd is listening. This is optional
98  * and if not specified, QEMU will send 0 which will make gluster to use the
99  * default port. If the transport type is unix, then 'port' should not be
100  * specified.
101  *
102  * 'volname' is the name of the gluster volume which contains the VM image.
103  *
104  * 'image' is the path to the actual VM image that resides on gluster volume.
105  *
106  * Examples:
107  *
108  * file=gluster://1.2.3.4/testvol/a.img
109  * file=gluster+tcp://1.2.3.4/testvol/a.img
110  * file=gluster+tcp://1.2.3.4:24007/testvol/dir/a.img
111  * file=gluster+tcp://[1:2:3:4:5:6:7:8]/testvol/dir/a.img
112  * file=gluster+tcp://[1:2:3:4:5:6:7:8]:24007/testvol/dir/a.img
113  * file=gluster+tcp://server.domain.com:24007/testvol/dir/a.img
114  * file=gluster+unix:///testvol/dir/a.img?socket=/tmp/glusterd.socket
115  * file=gluster+rdma://1.2.3.4:24007/testvol/a.img
116  */
117 static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
118 {
119     URI *uri;
120     QueryParams *qp = NULL;
121     bool is_unix = false;
122     int ret = 0;
123 
124     uri = uri_parse(filename);
125     if (!uri) {
126         return -EINVAL;
127     }
128 
129     /* transport */
130     if (!strcmp(uri->scheme, "gluster")) {
131         gconf->transport = g_strdup("tcp");
132     } else if (!strcmp(uri->scheme, "gluster+tcp")) {
133         gconf->transport = g_strdup("tcp");
134     } else if (!strcmp(uri->scheme, "gluster+unix")) {
135         gconf->transport = g_strdup("unix");
136         is_unix = true;
137     } else if (!strcmp(uri->scheme, "gluster+rdma")) {
138         gconf->transport = g_strdup("rdma");
139     } else {
140         ret = -EINVAL;
141         goto out;
142     }
143 
144     ret = parse_volume_options(gconf, uri->path);
145     if (ret < 0) {
146         goto out;
147     }
148 
149     qp = query_params_parse(uri->query);
150     if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) {
151         ret = -EINVAL;
152         goto out;
153     }
154 
155     if (is_unix) {
156         if (uri->server || uri->port) {
157             ret = -EINVAL;
158             goto out;
159         }
160         if (strcmp(qp->p[0].name, "socket")) {
161             ret = -EINVAL;
162             goto out;
163         }
164         gconf->server = g_strdup(qp->p[0].value);
165     } else {
166         gconf->server = g_strdup(uri->server);
167         gconf->port = uri->port;
168     }
169 
170 out:
171     if (qp) {
172         query_params_free(qp);
173     }
174     uri_free(uri);
175     return ret;
176 }
177 
178 static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
179 {
180     struct glfs *glfs = NULL;
181     int ret;
182     int old_errno;
183 
184     ret = qemu_gluster_parseuri(gconf, filename);
185     if (ret < 0) {
186         error_report("Usage: file=gluster[+transport]://[server[:port]]/"
187             "volname/image[?socket=...]");
188         errno = -ret;
189         goto out;
190     }
191 
192     glfs = glfs_new(gconf->volname);
193     if (!glfs) {
194         goto out;
195     }
196 
197     ret = glfs_set_volfile_server(glfs, gconf->transport, gconf->server,
198             gconf->port);
199     if (ret < 0) {
200         goto out;
201     }
202 
203     /*
204      * TODO: Use GF_LOG_ERROR instead of hard code value of 4 here when
205      * GlusterFS makes GF_LOG_* macros available to libgfapi users.
206      */
207     ret = glfs_set_logging(glfs, "-", 4);
208     if (ret < 0) {
209         goto out;
210     }
211 
212     ret = glfs_init(glfs);
213     if (ret) {
214         error_report("Gluster connection failed for server=%s port=%d "
215              "volume=%s image=%s transport=%s", gconf->server, gconf->port,
216              gconf->volname, gconf->image, gconf->transport);
217         goto out;
218     }
219     return glfs;
220 
221 out:
222     if (glfs) {
223         old_errno = errno;
224         glfs_fini(glfs);
225         errno = old_errno;
226     }
227     return NULL;
228 }
229 
230 static void qemu_gluster_complete_aio(void *opaque)
231 {
232     GlusterAIOCB *acb = (GlusterAIOCB *)opaque;
233 
234     qemu_bh_delete(acb->bh);
235     acb->bh = NULL;
236     qemu_coroutine_enter(acb->coroutine, NULL);
237 }
238 
239 /*
240  * AIO callback routine called from GlusterFS thread.
241  */
242 static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
243 {
244     GlusterAIOCB *acb = (GlusterAIOCB *)arg;
245 
246     if (!ret || ret == acb->size) {
247         acb->ret = 0; /* Success */
248     } else if (ret < 0) {
249         acb->ret = ret; /* Read/Write failed */
250     } else {
251         acb->ret = -EIO; /* Partial read/write - fail it */
252     }
253 
254     acb->bh = qemu_bh_new(qemu_gluster_complete_aio, acb);
255     qemu_bh_schedule(acb->bh);
256 }
257 
258 /* TODO Convert to fine grained options */
259 static QemuOptsList runtime_opts = {
260     .name = "gluster",
261     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
262     .desc = {
263         {
264             .name = "filename",
265             .type = QEMU_OPT_STRING,
266             .help = "URL to the gluster image",
267         },
268         { /* end of list */ }
269     },
270 };
271 
272 static int qemu_gluster_open(BlockDriverState *bs,  QDict *options,
273                              int bdrv_flags, Error **errp)
274 {
275     BDRVGlusterState *s = bs->opaque;
276     int open_flags = O_BINARY;
277     int ret = 0;
278     GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
279     QemuOpts *opts;
280     Error *local_err = NULL;
281     const char *filename;
282 
283     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
284     qemu_opts_absorb_qdict(opts, options, &local_err);
285     if (error_is_set(&local_err)) {
286         qerror_report_err(local_err);
287         error_free(local_err);
288         ret = -EINVAL;
289         goto out;
290     }
291 
292     filename = qemu_opt_get(opts, "filename");
293 
294     s->glfs = qemu_gluster_init(gconf, filename);
295     if (!s->glfs) {
296         ret = -errno;
297         goto out;
298     }
299 
300     if (bdrv_flags & BDRV_O_RDWR) {
301         open_flags |= O_RDWR;
302     } else {
303         open_flags |= O_RDONLY;
304     }
305 
306     if ((bdrv_flags & BDRV_O_NOCACHE)) {
307         open_flags |= O_DIRECT;
308     }
309 
310     s->fd = glfs_open(s->glfs, gconf->image, open_flags);
311     if (!s->fd) {
312         ret = -errno;
313     }
314 
315 out:
316     qemu_opts_del(opts);
317     qemu_gluster_gconf_free(gconf);
318     if (!ret) {
319         return ret;
320     }
321     if (s->fd) {
322         glfs_close(s->fd);
323     }
324     if (s->glfs) {
325         glfs_fini(s->glfs);
326     }
327     return ret;
328 }
329 
330 #ifdef CONFIG_GLUSTERFS_ZEROFILL
331 static coroutine_fn int qemu_gluster_co_write_zeroes(BlockDriverState *bs,
332         int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
333 {
334     int ret;
335     GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
336     BDRVGlusterState *s = bs->opaque;
337     off_t size = nb_sectors * BDRV_SECTOR_SIZE;
338     off_t offset = sector_num * BDRV_SECTOR_SIZE;
339 
340     acb->size = size;
341     acb->ret = 0;
342     acb->coroutine = qemu_coroutine_self();
343 
344     ret = glfs_zerofill_async(s->fd, offset, size, &gluster_finish_aiocb, acb);
345     if (ret < 0) {
346         ret = -errno;
347         goto out;
348     }
349 
350     qemu_coroutine_yield();
351     ret = acb->ret;
352 
353 out:
354     g_slice_free(GlusterAIOCB, acb);
355     return ret;
356 }
357 
358 static inline bool gluster_supports_zerofill(void)
359 {
360     return 1;
361 }
362 
363 static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
364         int64_t size)
365 {
366     return glfs_zerofill(fd, offset, size);
367 }
368 
369 #else
370 static inline bool gluster_supports_zerofill(void)
371 {
372     return 0;
373 }
374 
375 static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
376         int64_t size)
377 {
378     return 0;
379 }
380 #endif
381 
382 static int qemu_gluster_create(const char *filename,
383         QEMUOptionParameter *options, Error **errp)
384 {
385     struct glfs *glfs;
386     struct glfs_fd *fd;
387     int ret = 0;
388     int prealloc = 0;
389     int64_t total_size = 0;
390     GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
391 
392     glfs = qemu_gluster_init(gconf, filename);
393     if (!glfs) {
394         ret = -errno;
395         goto out;
396     }
397 
398     while (options && options->name) {
399         if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
400             total_size = options->value.n / BDRV_SECTOR_SIZE;
401         } else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) {
402             if (!options->value.s || !strcmp(options->value.s, "off")) {
403                 prealloc = 0;
404             } else if (!strcmp(options->value.s, "full") &&
405                     gluster_supports_zerofill()) {
406                 prealloc = 1;
407             } else {
408                 error_setg(errp, "Invalid preallocation mode: '%s'"
409                     " or GlusterFS doesn't support zerofill API",
410                            options->value.s);
411                 ret = -EINVAL;
412                 goto out;
413             }
414         }
415         options++;
416     }
417 
418     fd = glfs_creat(glfs, gconf->image,
419         O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
420     if (!fd) {
421         ret = -errno;
422     } else {
423         if (!glfs_ftruncate(fd, total_size * BDRV_SECTOR_SIZE)) {
424             if (prealloc && qemu_gluster_zerofill(fd, 0,
425                     total_size * BDRV_SECTOR_SIZE)) {
426                 ret = -errno;
427             }
428         } else {
429             ret = -errno;
430         }
431 
432         if (glfs_close(fd) != 0) {
433             ret = -errno;
434         }
435     }
436 out:
437     qemu_gluster_gconf_free(gconf);
438     if (glfs) {
439         glfs_fini(glfs);
440     }
441     return ret;
442 }
443 
444 static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
445         int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int write)
446 {
447     int ret;
448     GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
449     BDRVGlusterState *s = bs->opaque;
450     size_t size = nb_sectors * BDRV_SECTOR_SIZE;
451     off_t offset = sector_num * BDRV_SECTOR_SIZE;
452 
453     acb->size = size;
454     acb->ret = 0;
455     acb->coroutine = qemu_coroutine_self();
456 
457     if (write) {
458         ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
459             &gluster_finish_aiocb, acb);
460     } else {
461         ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0,
462             &gluster_finish_aiocb, acb);
463     }
464 
465     if (ret < 0) {
466         ret = -errno;
467         goto out;
468     }
469 
470     qemu_coroutine_yield();
471     ret = acb->ret;
472 
473 out:
474     g_slice_free(GlusterAIOCB, acb);
475     return ret;
476 }
477 
478 static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset)
479 {
480     int ret;
481     BDRVGlusterState *s = bs->opaque;
482 
483     ret = glfs_ftruncate(s->fd, offset);
484     if (ret < 0) {
485         return -errno;
486     }
487 
488     return 0;
489 }
490 
491 static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,
492         int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
493 {
494     return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 0);
495 }
496 
497 static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs,
498         int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
499 {
500     return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1);
501 }
502 
503 static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs)
504 {
505     int ret;
506     GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
507     BDRVGlusterState *s = bs->opaque;
508 
509     acb->size = 0;
510     acb->ret = 0;
511     acb->coroutine = qemu_coroutine_self();
512 
513     ret = glfs_fsync_async(s->fd, &gluster_finish_aiocb, acb);
514     if (ret < 0) {
515         ret = -errno;
516         goto out;
517     }
518 
519     qemu_coroutine_yield();
520     ret = acb->ret;
521 
522 out:
523     g_slice_free(GlusterAIOCB, acb);
524     return ret;
525 }
526 
527 #ifdef CONFIG_GLUSTERFS_DISCARD
528 static coroutine_fn int qemu_gluster_co_discard(BlockDriverState *bs,
529         int64_t sector_num, int nb_sectors)
530 {
531     int ret;
532     GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
533     BDRVGlusterState *s = bs->opaque;
534     size_t size = nb_sectors * BDRV_SECTOR_SIZE;
535     off_t offset = sector_num * BDRV_SECTOR_SIZE;
536 
537     acb->size = 0;
538     acb->ret = 0;
539     acb->coroutine = qemu_coroutine_self();
540 
541     ret = glfs_discard_async(s->fd, offset, size, &gluster_finish_aiocb, acb);
542     if (ret < 0) {
543         ret = -errno;
544         goto out;
545     }
546 
547     qemu_coroutine_yield();
548     ret = acb->ret;
549 
550 out:
551     g_slice_free(GlusterAIOCB, acb);
552     return ret;
553 }
554 #endif
555 
556 static int64_t qemu_gluster_getlength(BlockDriverState *bs)
557 {
558     BDRVGlusterState *s = bs->opaque;
559     int64_t ret;
560 
561     ret = glfs_lseek(s->fd, 0, SEEK_END);
562     if (ret < 0) {
563         return -errno;
564     } else {
565         return ret;
566     }
567 }
568 
569 static int64_t qemu_gluster_allocated_file_size(BlockDriverState *bs)
570 {
571     BDRVGlusterState *s = bs->opaque;
572     struct stat st;
573     int ret;
574 
575     ret = glfs_fstat(s->fd, &st);
576     if (ret < 0) {
577         return -errno;
578     } else {
579         return st.st_blocks * 512;
580     }
581 }
582 
583 static void qemu_gluster_close(BlockDriverState *bs)
584 {
585     BDRVGlusterState *s = bs->opaque;
586 
587     if (s->fd) {
588         glfs_close(s->fd);
589         s->fd = NULL;
590     }
591     glfs_fini(s->glfs);
592 }
593 
594 static int qemu_gluster_has_zero_init(BlockDriverState *bs)
595 {
596     /* GlusterFS volume could be backed by a block device */
597     return 0;
598 }
599 
600 static QEMUOptionParameter qemu_gluster_create_options[] = {
601     {
602         .name = BLOCK_OPT_SIZE,
603         .type = OPT_SIZE,
604         .help = "Virtual disk size"
605     },
606     {
607         .name = BLOCK_OPT_PREALLOC,
608         .type = OPT_STRING,
609         .help = "Preallocation mode (allowed values: off, full)"
610     },
611     { NULL }
612 };
613 
614 static BlockDriver bdrv_gluster = {
615     .format_name                  = "gluster",
616     .protocol_name                = "gluster",
617     .instance_size                = sizeof(BDRVGlusterState),
618     .bdrv_needs_filename          = true,
619     .bdrv_file_open               = qemu_gluster_open,
620     .bdrv_close                   = qemu_gluster_close,
621     .bdrv_create                  = qemu_gluster_create,
622     .bdrv_getlength               = qemu_gluster_getlength,
623     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
624     .bdrv_truncate                = qemu_gluster_truncate,
625     .bdrv_co_readv                = qemu_gluster_co_readv,
626     .bdrv_co_writev               = qemu_gluster_co_writev,
627     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk,
628     .bdrv_has_zero_init           = qemu_gluster_has_zero_init,
629 #ifdef CONFIG_GLUSTERFS_DISCARD
630     .bdrv_co_discard              = qemu_gluster_co_discard,
631 #endif
632 #ifdef CONFIG_GLUSTERFS_ZEROFILL
633     .bdrv_co_write_zeroes         = qemu_gluster_co_write_zeroes,
634 #endif
635     .create_options               = qemu_gluster_create_options,
636 };
637 
638 static BlockDriver bdrv_gluster_tcp = {
639     .format_name                  = "gluster",
640     .protocol_name                = "gluster+tcp",
641     .instance_size                = sizeof(BDRVGlusterState),
642     .bdrv_needs_filename          = true,
643     .bdrv_file_open               = qemu_gluster_open,
644     .bdrv_close                   = qemu_gluster_close,
645     .bdrv_create                  = qemu_gluster_create,
646     .bdrv_getlength               = qemu_gluster_getlength,
647     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
648     .bdrv_truncate                = qemu_gluster_truncate,
649     .bdrv_co_readv                = qemu_gluster_co_readv,
650     .bdrv_co_writev               = qemu_gluster_co_writev,
651     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk,
652     .bdrv_has_zero_init           = qemu_gluster_has_zero_init,
653 #ifdef CONFIG_GLUSTERFS_DISCARD
654     .bdrv_co_discard              = qemu_gluster_co_discard,
655 #endif
656 #ifdef CONFIG_GLUSTERFS_ZEROFILL
657     .bdrv_co_write_zeroes         = qemu_gluster_co_write_zeroes,
658 #endif
659     .create_options               = qemu_gluster_create_options,
660 };
661 
662 static BlockDriver bdrv_gluster_unix = {
663     .format_name                  = "gluster",
664     .protocol_name                = "gluster+unix",
665     .instance_size                = sizeof(BDRVGlusterState),
666     .bdrv_needs_filename          = true,
667     .bdrv_file_open               = qemu_gluster_open,
668     .bdrv_close                   = qemu_gluster_close,
669     .bdrv_create                  = qemu_gluster_create,
670     .bdrv_getlength               = qemu_gluster_getlength,
671     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
672     .bdrv_truncate                = qemu_gluster_truncate,
673     .bdrv_co_readv                = qemu_gluster_co_readv,
674     .bdrv_co_writev               = qemu_gluster_co_writev,
675     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk,
676     .bdrv_has_zero_init           = qemu_gluster_has_zero_init,
677 #ifdef CONFIG_GLUSTERFS_DISCARD
678     .bdrv_co_discard              = qemu_gluster_co_discard,
679 #endif
680 #ifdef CONFIG_GLUSTERFS_ZEROFILL
681     .bdrv_co_write_zeroes         = qemu_gluster_co_write_zeroes,
682 #endif
683     .create_options               = qemu_gluster_create_options,
684 };
685 
686 static BlockDriver bdrv_gluster_rdma = {
687     .format_name                  = "gluster",
688     .protocol_name                = "gluster+rdma",
689     .instance_size                = sizeof(BDRVGlusterState),
690     .bdrv_needs_filename          = true,
691     .bdrv_file_open               = qemu_gluster_open,
692     .bdrv_close                   = qemu_gluster_close,
693     .bdrv_create                  = qemu_gluster_create,
694     .bdrv_getlength               = qemu_gluster_getlength,
695     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
696     .bdrv_truncate                = qemu_gluster_truncate,
697     .bdrv_co_readv                = qemu_gluster_co_readv,
698     .bdrv_co_writev               = qemu_gluster_co_writev,
699     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk,
700     .bdrv_has_zero_init           = qemu_gluster_has_zero_init,
701 #ifdef CONFIG_GLUSTERFS_DISCARD
702     .bdrv_co_discard              = qemu_gluster_co_discard,
703 #endif
704 #ifdef CONFIG_GLUSTERFS_ZEROFILL
705     .bdrv_co_write_zeroes         = qemu_gluster_co_write_zeroes,
706 #endif
707     .create_options               = qemu_gluster_create_options,
708 };
709 
710 static void bdrv_gluster_init(void)
711 {
712     bdrv_register(&bdrv_gluster_rdma);
713     bdrv_register(&bdrv_gluster_unix);
714     bdrv_register(&bdrv_gluster_tcp);
715     bdrv_register(&bdrv_gluster);
716 }
717 
718 block_init(bdrv_gluster_init);
719