1
2
3
4
5
6
7
8
9
10
11#include "qemu/osdep.h"
12#include <glusterfs/api/glfs.h>
13#include "block/block_int.h"
14#include "qapi/error.h"
15#include "qapi/qmp/qdict.h"
16#include "qapi/qmp/qerror.h"
17#include "qemu/uri.h"
18#include "qemu/error-report.h"
19#include "qemu/option.h"
20#include "qemu/cutils.h"
21
22#define GLUSTER_OPT_FILENAME "filename"
23#define GLUSTER_OPT_VOLUME "volume"
24#define GLUSTER_OPT_PATH "path"
25#define GLUSTER_OPT_TYPE "type"
26#define GLUSTER_OPT_SERVER_PATTERN "server."
27#define GLUSTER_OPT_HOST "host"
28#define GLUSTER_OPT_PORT "port"
29#define GLUSTER_OPT_TO "to"
30#define GLUSTER_OPT_IPV4 "ipv4"
31#define GLUSTER_OPT_IPV6 "ipv6"
32#define GLUSTER_OPT_SOCKET "socket"
33#define GLUSTER_OPT_DEBUG "debug"
34#define GLUSTER_DEFAULT_PORT 24007
35#define GLUSTER_DEBUG_DEFAULT 4
36#define GLUSTER_DEBUG_MAX 9
37#define GLUSTER_OPT_LOGFILE "logfile"
38#define GLUSTER_LOGFILE_DEFAULT "-"
39
40#define GERR_INDEX_HINT "hint: check in 'server' array index '%d'\n"
41
42typedef struct GlusterAIOCB {
43 int64_t size;
44 int ret;
45 Coroutine *coroutine;
46 AioContext *aio_context;
47} GlusterAIOCB;
48
49typedef struct BDRVGlusterState {
50 struct glfs *glfs;
51 struct glfs_fd *fd;
52 char *logfile;
53 bool supports_seek_data;
54 int debug;
55} BDRVGlusterState;
56
57typedef struct BDRVGlusterReopenState {
58 struct glfs *glfs;
59 struct glfs_fd *fd;
60} BDRVGlusterReopenState;
61
62
63typedef struct GlfsPreopened {
64 char *volume;
65 glfs_t *fs;
66 int ref;
67} GlfsPreopened;
68
69typedef struct ListElement {
70 QLIST_ENTRY(ListElement) list;
71 GlfsPreopened saved;
72} ListElement;
73
74static QLIST_HEAD(glfs_list, ListElement) glfs_list;
75
76static QemuOptsList qemu_gluster_create_opts = {
77 .name = "qemu-gluster-create-opts",
78 .head = QTAILQ_HEAD_INITIALIZER(qemu_gluster_create_opts.head),
79 .desc = {
80 {
81 .name = BLOCK_OPT_SIZE,
82 .type = QEMU_OPT_SIZE,
83 .help = "Virtual disk size"
84 },
85 {
86 .name = BLOCK_OPT_PREALLOC,
87 .type = QEMU_OPT_STRING,
88 .help = "Preallocation mode (allowed values: off, full)"
89 },
90 {
91 .name = GLUSTER_OPT_DEBUG,
92 .type = QEMU_OPT_NUMBER,
93 .help = "Gluster log level, valid range is 0-9",
94 },
95 {
96 .name = GLUSTER_OPT_LOGFILE,
97 .type = QEMU_OPT_STRING,
98 .help = "Logfile path of libgfapi",
99 },
100 { }
101 }
102};
103
104static QemuOptsList runtime_opts = {
105 .name = "gluster",
106 .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
107 .desc = {
108 {
109 .name = GLUSTER_OPT_FILENAME,
110 .type = QEMU_OPT_STRING,
111 .help = "URL to the gluster image",
112 },
113 {
114 .name = GLUSTER_OPT_DEBUG,
115 .type = QEMU_OPT_NUMBER,
116 .help = "Gluster log level, valid range is 0-9",
117 },
118 {
119 .name = GLUSTER_OPT_LOGFILE,
120 .type = QEMU_OPT_STRING,
121 .help = "Logfile path of libgfapi",
122 },
123 { }
124 },
125};
126
127static QemuOptsList runtime_json_opts = {
128 .name = "gluster_json",
129 .head = QTAILQ_HEAD_INITIALIZER(runtime_json_opts.head),
130 .desc = {
131 {
132 .name = GLUSTER_OPT_VOLUME,
133 .type = QEMU_OPT_STRING,
134 .help = "name of gluster volume where VM image resides",
135 },
136 {
137 .name = GLUSTER_OPT_PATH,
138 .type = QEMU_OPT_STRING,
139 .help = "absolute path to image file in gluster volume",
140 },
141 {
142 .name = GLUSTER_OPT_DEBUG,
143 .type = QEMU_OPT_NUMBER,
144 .help = "Gluster log level, valid range is 0-9",
145 },
146 { }
147 },
148};
149
150static QemuOptsList runtime_type_opts = {
151 .name = "gluster_type",
152 .head = QTAILQ_HEAD_INITIALIZER(runtime_type_opts.head),
153 .desc = {
154 {
155 .name = GLUSTER_OPT_TYPE,
156 .type = QEMU_OPT_STRING,
157 .help = "inet|unix",
158 },
159 { }
160 },
161};
162
163static QemuOptsList runtime_unix_opts = {
164 .name = "gluster_unix",
165 .head = QTAILQ_HEAD_INITIALIZER(runtime_unix_opts.head),
166 .desc = {
167 {
168 .name = GLUSTER_OPT_SOCKET,
169 .type = QEMU_OPT_STRING,
170 .help = "socket file path (legacy)",
171 },
172 {
173 .name = GLUSTER_OPT_PATH,
174 .type = QEMU_OPT_STRING,
175 .help = "socket file path (QAPI)",
176 },
177 { }
178 },
179};
180
181static QemuOptsList runtime_inet_opts = {
182 .name = "gluster_inet",
183 .head = QTAILQ_HEAD_INITIALIZER(runtime_inet_opts.head),
184 .desc = {
185 {
186 .name = GLUSTER_OPT_TYPE,
187 .type = QEMU_OPT_STRING,
188 .help = "inet|unix",
189 },
190 {
191 .name = GLUSTER_OPT_HOST,
192 .type = QEMU_OPT_STRING,
193 .help = "host address (hostname/ipv4/ipv6 addresses)",
194 },
195 {
196 .name = GLUSTER_OPT_PORT,
197 .type = QEMU_OPT_STRING,
198 .help = "port number on which glusterd is listening (default 24007)",
199 },
200 {
201 .name = "to",
202 .type = QEMU_OPT_NUMBER,
203 .help = "max port number, not supported by gluster",
204 },
205 {
206 .name = "ipv4",
207 .type = QEMU_OPT_BOOL,
208 .help = "ipv4 bool value, not supported by gluster",
209 },
210 {
211 .name = "ipv6",
212 .type = QEMU_OPT_BOOL,
213 .help = "ipv6 bool value, not supported by gluster",
214 },
215 { }
216 },
217};
218
219static void glfs_set_preopened(const char *volume, glfs_t *fs)
220{
221 ListElement *entry = NULL;
222
223 entry = g_new(ListElement, 1);
224
225 entry->saved.volume = g_strdup(volume);
226
227 entry->saved.fs = fs;
228 entry->saved.ref = 1;
229
230 QLIST_INSERT_HEAD(&glfs_list, entry, list);
231}
232
233static glfs_t *glfs_find_preopened(const char *volume)
234{
235 ListElement *entry = NULL;
236
237 QLIST_FOREACH(entry, &glfs_list, list) {
238 if (strcmp(entry->saved.volume, volume) == 0) {
239 entry->saved.ref++;
240 return entry->saved.fs;
241 }
242 }
243
244 return NULL;
245}
246
247static void glfs_clear_preopened(glfs_t *fs)
248{
249 ListElement *entry = NULL;
250 ListElement *next;
251
252 if (fs == NULL) {
253 return;
254 }
255
256 QLIST_FOREACH_SAFE(entry, &glfs_list, list, next) {
257 if (entry->saved.fs == fs) {
258 if (--entry->saved.ref) {
259 return;
260 }
261
262 QLIST_REMOVE(entry, list);
263
264 glfs_fini(entry->saved.fs);
265 g_free(entry->saved.volume);
266 g_free(entry);
267 }
268 }
269}
270
271static int parse_volume_options(BlockdevOptionsGluster *gconf, char *path)
272{
273 char *p, *q;
274
275 if (!path) {
276 return -EINVAL;
277 }
278
279
280 p = q = path + strspn(path, "/");
281 p += strcspn(p, "/");
282 if (*p == '\0') {
283 return -EINVAL;
284 }
285 gconf->volume = g_strndup(q, p - q);
286
287
288 p += strspn(p, "/");
289 if (*p == '\0') {
290 return -EINVAL;
291 }
292 gconf->path = g_strdup(p);
293 return 0;
294}
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
329 const char *filename)
330{
331 SocketAddress *gsconf;
332 URI *uri;
333 QueryParams *qp = NULL;
334 bool is_unix = false;
335 int ret = 0;
336
337 uri = uri_parse(filename);
338 if (!uri) {
339 return -EINVAL;
340 }
341
342 gconf->server = g_new0(SocketAddressList, 1);
343 gconf->server->value = gsconf = g_new0(SocketAddress, 1);
344
345
346 if (!uri->scheme || !strcmp(uri->scheme, "gluster")) {
347 gsconf->type = SOCKET_ADDRESS_TYPE_INET;
348 } else if (!strcmp(uri->scheme, "gluster+tcp")) {
349 gsconf->type = SOCKET_ADDRESS_TYPE_INET;
350 } else if (!strcmp(uri->scheme, "gluster+unix")) {
351 gsconf->type = SOCKET_ADDRESS_TYPE_UNIX;
352 is_unix = true;
353 } else if (!strcmp(uri->scheme, "gluster+rdma")) {
354 gsconf->type = SOCKET_ADDRESS_TYPE_INET;
355 warn_report("rdma feature is not supported, falling back to tcp");
356 } else {
357 ret = -EINVAL;
358 goto out;
359 }
360
361 ret = parse_volume_options(gconf, uri->path);
362 if (ret < 0) {
363 goto out;
364 }
365
366 qp = query_params_parse(uri->query);
367 if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) {
368 ret = -EINVAL;
369 goto out;
370 }
371
372 if (is_unix) {
373 if (uri->server || uri->port) {
374 ret = -EINVAL;
375 goto out;
376 }
377 if (strcmp(qp->p[0].name, "socket")) {
378 ret = -EINVAL;
379 goto out;
380 }
381 gsconf->u.q_unix.path = g_strdup(qp->p[0].value);
382 } else {
383 gsconf->u.inet.host = g_strdup(uri->server ? uri->server : "localhost");
384 if (uri->port) {
385 gsconf->u.inet.port = g_strdup_printf("%d", uri->port);
386 } else {
387 gsconf->u.inet.port = g_strdup_printf("%d", GLUSTER_DEFAULT_PORT);
388 }
389 }
390
391out:
392 if (qp) {
393 query_params_free(qp);
394 }
395 uri_free(uri);
396 return ret;
397}
398
399static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
400 Error **errp)
401{
402 struct glfs *glfs;
403 int ret;
404 int old_errno;
405 SocketAddressList *server;
406 unsigned long long port;
407
408 glfs = glfs_find_preopened(gconf->volume);
409 if (glfs) {
410 return glfs;
411 }
412
413 glfs = glfs_new(gconf->volume);
414 if (!glfs) {
415 goto out;
416 }
417
418 glfs_set_preopened(gconf->volume, glfs);
419
420 for (server = gconf->server; server; server = server->next) {
421 switch (server->value->type) {
422 case SOCKET_ADDRESS_TYPE_UNIX:
423 ret = glfs_set_volfile_server(glfs, "unix",
424 server->value->u.q_unix.path, 0);
425 break;
426 case SOCKET_ADDRESS_TYPE_INET:
427 if (parse_uint_full(server->value->u.inet.port, &port, 10) < 0 ||
428 port > 65535) {
429 error_setg(errp, "'%s' is not a valid port number",
430 server->value->u.inet.port);
431 errno = EINVAL;
432 goto out;
433 }
434 ret = glfs_set_volfile_server(glfs, "tcp",
435 server->value->u.inet.host,
436 (int)port);
437 break;
438 case SOCKET_ADDRESS_TYPE_VSOCK:
439 case SOCKET_ADDRESS_TYPE_FD:
440 default:
441 abort();
442 }
443
444 if (ret < 0) {
445 goto out;
446 }
447 }
448
449 ret = glfs_set_logging(glfs, gconf->logfile, gconf->debug);
450 if (ret < 0) {
451 goto out;
452 }
453
454 ret = glfs_init(glfs);
455 if (ret) {
456 error_setg(errp, "Gluster connection for volume %s, path %s failed"
457 " to connect", gconf->volume, gconf->path);
458 for (server = gconf->server; server; server = server->next) {
459 if (server->value->type == SOCKET_ADDRESS_TYPE_UNIX) {
460 error_append_hint(errp, "hint: failed on socket %s ",
461 server->value->u.q_unix.path);
462 } else {
463 error_append_hint(errp, "hint: failed on host %s and port %s ",
464 server->value->u.inet.host,
465 server->value->u.inet.port);
466 }
467 }
468
469 error_append_hint(errp, "Please refer to gluster logs for more info\n");
470
471
472 if (errno == 0) {
473 errno = EINVAL;
474 }
475
476 goto out;
477 }
478 return glfs;
479
480out:
481 if (glfs) {
482 old_errno = errno;
483 glfs_clear_preopened(glfs);
484 errno = old_errno;
485 }
486 return NULL;
487}
488
489
490
491
492static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
493 QDict *options, Error **errp)
494{
495 QemuOpts *opts;
496 SocketAddress *gsconf = NULL;
497 SocketAddressList *curr = NULL;
498 QDict *backing_options = NULL;
499 Error *local_err = NULL;
500 char *str = NULL;
501 const char *ptr;
502 int i, type, num_servers;
503
504
505 opts = qemu_opts_create(&runtime_json_opts, NULL, 0, &error_abort);
506 qemu_opts_absorb_qdict(opts, options, &local_err);
507 if (local_err) {
508 goto out;
509 }
510
511 num_servers = qdict_array_entries(options, GLUSTER_OPT_SERVER_PATTERN);
512 if (num_servers < 1) {
513 error_setg(&local_err, QERR_MISSING_PARAMETER, "server");
514 goto out;
515 }
516
517 ptr = qemu_opt_get(opts, GLUSTER_OPT_VOLUME);
518 if (!ptr) {
519 error_setg(&local_err, QERR_MISSING_PARAMETER, GLUSTER_OPT_VOLUME);
520 goto out;
521 }
522 gconf->volume = g_strdup(ptr);
523
524 ptr = qemu_opt_get(opts, GLUSTER_OPT_PATH);
525 if (!ptr) {
526 error_setg(&local_err, QERR_MISSING_PARAMETER, GLUSTER_OPT_PATH);
527 goto out;
528 }
529 gconf->path = g_strdup(ptr);
530 qemu_opts_del(opts);
531
532 for (i = 0; i < num_servers; i++) {
533 str = g_strdup_printf(GLUSTER_OPT_SERVER_PATTERN"%d.", i);
534 qdict_extract_subqdict(options, &backing_options, str);
535
536
537 opts = qemu_opts_create(&runtime_type_opts, NULL, 0, &error_abort);
538 qemu_opts_absorb_qdict(opts, backing_options, &local_err);
539 if (local_err) {
540 goto out;
541 }
542
543 ptr = qemu_opt_get(opts, GLUSTER_OPT_TYPE);
544 if (!ptr) {
545 error_setg(&local_err, QERR_MISSING_PARAMETER, GLUSTER_OPT_TYPE);
546 error_append_hint(&local_err, GERR_INDEX_HINT, i);
547 goto out;
548
549 }
550 gsconf = g_new0(SocketAddress, 1);
551 if (!strcmp(ptr, "tcp")) {
552 ptr = "inet";
553 }
554 type = qapi_enum_parse(&SocketAddressType_lookup, ptr, -1, NULL);
555 if (type != SOCKET_ADDRESS_TYPE_INET
556 && type != SOCKET_ADDRESS_TYPE_UNIX) {
557 error_setg(&local_err,
558 "Parameter '%s' may be 'inet' or 'unix'",
559 GLUSTER_OPT_TYPE);
560 error_append_hint(&local_err, GERR_INDEX_HINT, i);
561 goto out;
562 }
563 gsconf->type = type;
564 qemu_opts_del(opts);
565
566 if (gsconf->type == SOCKET_ADDRESS_TYPE_INET) {
567
568 opts = qemu_opts_create(&runtime_inet_opts, NULL, 0, &error_abort);
569 qemu_opts_absorb_qdict(opts, backing_options, &local_err);
570 if (local_err) {
571 goto out;
572 }
573
574 ptr = qemu_opt_get(opts, GLUSTER_OPT_HOST);
575 if (!ptr) {
576 error_setg(&local_err, QERR_MISSING_PARAMETER,
577 GLUSTER_OPT_HOST);
578 error_append_hint(&local_err, GERR_INDEX_HINT, i);
579 goto out;
580 }
581 gsconf->u.inet.host = g_strdup(ptr);
582 ptr = qemu_opt_get(opts, GLUSTER_OPT_PORT);
583 if (!ptr) {
584 error_setg(&local_err, QERR_MISSING_PARAMETER,
585 GLUSTER_OPT_PORT);
586 error_append_hint(&local_err, GERR_INDEX_HINT, i);
587 goto out;
588 }
589 gsconf->u.inet.port = g_strdup(ptr);
590
591
592
593
594 ptr = qemu_opt_get(opts, GLUSTER_OPT_TO);
595 if (ptr) {
596 gsconf->u.inet.has_to = true;
597 }
598 ptr = qemu_opt_get(opts, GLUSTER_OPT_IPV4);
599 if (ptr) {
600 gsconf->u.inet.has_ipv4 = true;
601 }
602 ptr = qemu_opt_get(opts, GLUSTER_OPT_IPV6);
603 if (ptr) {
604 gsconf->u.inet.has_ipv6 = true;
605 }
606 if (gsconf->u.inet.has_to) {
607 error_setg(&local_err, "Parameter 'to' not supported");
608 goto out;
609 }
610 if (gsconf->u.inet.has_ipv4 || gsconf->u.inet.has_ipv6) {
611 error_setg(&local_err, "Parameters 'ipv4/ipv6' not supported");
612 goto out;
613 }
614 qemu_opts_del(opts);
615 } else {
616
617 opts = qemu_opts_create(&runtime_unix_opts, NULL, 0, &error_abort);
618 qemu_opts_absorb_qdict(opts, backing_options, &local_err);
619 if (local_err) {
620 goto out;
621 }
622
623 ptr = qemu_opt_get(opts, GLUSTER_OPT_PATH);
624 if (!ptr) {
625 ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET);
626 } else if (qemu_opt_get(opts, GLUSTER_OPT_SOCKET)) {
627 error_setg(&local_err,
628 "Conflicting parameters 'path' and 'socket'");
629 error_append_hint(&local_err, GERR_INDEX_HINT, i);
630 goto out;
631 }
632 if (!ptr) {
633 error_setg(&local_err, QERR_MISSING_PARAMETER,
634 GLUSTER_OPT_PATH);
635 error_append_hint(&local_err, GERR_INDEX_HINT, i);
636 goto out;
637 }
638 gsconf->u.q_unix.path = g_strdup(ptr);
639 qemu_opts_del(opts);
640 }
641
642 if (gconf->server == NULL) {
643 gconf->server = g_new0(SocketAddressList, 1);
644 gconf->server->value = gsconf;
645 curr = gconf->server;
646 } else {
647 curr->next = g_new0(SocketAddressList, 1);
648 curr->next->value = gsconf;
649 curr = curr->next;
650 }
651 gsconf = NULL;
652
653 QDECREF(backing_options);
654 backing_options = NULL;
655 g_free(str);
656 str = NULL;
657 }
658
659 return 0;
660
661out:
662 error_propagate(errp, local_err);
663 qapi_free_SocketAddress(gsconf);
664 qemu_opts_del(opts);
665 g_free(str);
666 QDECREF(backing_options);
667 errno = EINVAL;
668 return -errno;
669}
670
671
672
673static int qemu_gluster_parse(BlockdevOptionsGluster *gconf,
674 const char *filename,
675 QDict *options, Error **errp)
676{
677 int ret;
678 if (filename) {
679 ret = qemu_gluster_parse_uri(gconf, filename);
680 if (ret < 0) {
681 error_setg(errp, "invalid URI %s", filename);
682 error_append_hint(errp, "Usage: file=gluster[+transport]://"
683 "[host[:port]]volume/path[?socket=...]"
684 "[,file.debug=N]"
685 "[,file.logfile=/path/filename.log]\n");
686 return ret;
687 }
688 } else {
689 ret = qemu_gluster_parse_json(gconf, options, errp);
690 if (ret < 0) {
691 error_append_hint(errp, "Usage: "
692 "-drive driver=qcow2,file.driver=gluster,"
693 "file.volume=testvol,file.path=/path/a.qcow2"
694 "[,file.debug=9]"
695 "[,file.logfile=/path/filename.log],"
696 "file.server.0.type=inet,"
697 "file.server.0.host=1.2.3.4,"
698 "file.server.0.port=24007,"
699 "file.server.1.transport=unix,"
700 "file.server.1.path=/var/run/glusterd.socket ..."
701 "\n");
702 return ret;
703 }
704 }
705
706 return 0;
707}
708
709static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
710 const char *filename,
711 QDict *options, Error **errp)
712{
713 int ret;
714
715 ret = qemu_gluster_parse(gconf, filename, options, errp);
716 if (ret < 0) {
717 errno = -ret;
718 return NULL;
719 }
720
721 return qemu_gluster_glfs_init(gconf, errp);
722}
723
724
725
726
727static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
728{
729 GlusterAIOCB *acb = (GlusterAIOCB *)arg;
730
731 if (!ret || ret == acb->size) {
732 acb->ret = 0;
733 } else if (ret < 0) {
734 acb->ret = -errno;
735 } else {
736 acb->ret = -EIO;
737 }
738
739 aio_co_schedule(acb->aio_context, acb->coroutine);
740}
741
742static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags)
743{
744 assert(open_flags != NULL);
745
746 *open_flags |= O_BINARY;
747
748 if (bdrv_flags & BDRV_O_RDWR) {
749 *open_flags |= O_RDWR;
750 } else {
751 *open_flags |= O_RDONLY;
752 }
753
754 if ((bdrv_flags & BDRV_O_NOCACHE)) {
755 *open_flags |= O_DIRECT;
756 }
757}
758
759
760
761
762
763
764
765
766static bool qemu_gluster_test_seek(struct glfs_fd *fd)
767{
768 off_t ret = 0;
769
770#if defined SEEK_HOLE && defined SEEK_DATA
771 off_t eof;
772
773 eof = glfs_lseek(fd, 0, SEEK_END);
774 if (eof < 0) {
775
776 return false;
777 }
778
779
780 ret = glfs_lseek(fd, eof, SEEK_DATA);
781#endif
782
783 return (ret < 0) && (errno == ENXIO);
784}
785
786static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
787 int bdrv_flags, Error **errp)
788{
789 BDRVGlusterState *s = bs->opaque;
790 int open_flags = 0;
791 int ret = 0;
792 BlockdevOptionsGluster *gconf = NULL;
793 QemuOpts *opts;
794 Error *local_err = NULL;
795 const char *filename, *logfile;
796
797 opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
798 qemu_opts_absorb_qdict(opts, options, &local_err);
799 if (local_err) {
800 error_propagate(errp, local_err);
801 ret = -EINVAL;
802 goto out;
803 }
804
805 filename = qemu_opt_get(opts, GLUSTER_OPT_FILENAME);
806
807 s->debug = qemu_opt_get_number(opts, GLUSTER_OPT_DEBUG,
808 GLUSTER_DEBUG_DEFAULT);
809 if (s->debug < 0) {
810 s->debug = 0;
811 } else if (s->debug > GLUSTER_DEBUG_MAX) {
812 s->debug = GLUSTER_DEBUG_MAX;
813 }
814
815 gconf = g_new0(BlockdevOptionsGluster, 1);
816 gconf->debug = s->debug;
817 gconf->has_debug = true;
818
819 logfile = qemu_opt_get(opts, GLUSTER_OPT_LOGFILE);
820 s->logfile = g_strdup(logfile ? logfile : GLUSTER_LOGFILE_DEFAULT);
821
822 gconf->logfile = g_strdup(s->logfile);
823 gconf->has_logfile = true;
824
825 s->glfs = qemu_gluster_init(gconf, filename, options, errp);
826 if (!s->glfs) {
827 ret = -errno;
828 goto out;
829 }
830
831#ifdef CONFIG_GLUSTERFS_XLATOR_OPT
832
833
834
835
836
837
838 ret = glfs_set_xlator_option(s->glfs, "*-write-behind",
839 "resync-failed-syncs-after-fsync",
840 "on");
841 if (ret < 0) {
842 error_setg_errno(errp, errno, "Unable to set xlator key/value pair");
843 ret = -errno;
844 goto out;
845 }
846#endif
847
848 qemu_gluster_parse_flags(bdrv_flags, &open_flags);
849
850 s->fd = glfs_open(s->glfs, gconf->path, open_flags);
851 if (!s->fd) {
852 ret = -errno;
853 }
854
855 s->supports_seek_data = qemu_gluster_test_seek(s->fd);
856
857out:
858 qemu_opts_del(opts);
859 qapi_free_BlockdevOptionsGluster(gconf);
860 if (!ret) {
861 return ret;
862 }
863 g_free(s->logfile);
864 if (s->fd) {
865 glfs_close(s->fd);
866 }
867
868 glfs_clear_preopened(s->glfs);
869
870 return ret;
871}
872
873static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
874 BlockReopenQueue *queue, Error **errp)
875{
876 int ret = 0;
877 BDRVGlusterState *s;
878 BDRVGlusterReopenState *reop_s;
879 BlockdevOptionsGluster *gconf;
880 int open_flags = 0;
881
882 assert(state != NULL);
883 assert(state->bs != NULL);
884
885 s = state->bs->opaque;
886
887 state->opaque = g_new0(BDRVGlusterReopenState, 1);
888 reop_s = state->opaque;
889
890 qemu_gluster_parse_flags(state->flags, &open_flags);
891
892 gconf = g_new0(BlockdevOptionsGluster, 1);
893 gconf->debug = s->debug;
894 gconf->has_debug = true;
895 gconf->logfile = g_strdup(s->logfile);
896 gconf->has_logfile = true;
897 reop_s->glfs = qemu_gluster_init(gconf, state->bs->filename, NULL, errp);
898 if (reop_s->glfs == NULL) {
899 ret = -errno;
900 goto exit;
901 }
902
903#ifdef CONFIG_GLUSTERFS_XLATOR_OPT
904 ret = glfs_set_xlator_option(reop_s->glfs, "*-write-behind",
905 "resync-failed-syncs-after-fsync", "on");
906 if (ret < 0) {
907 error_setg_errno(errp, errno, "Unable to set xlator key/value pair");
908 ret = -errno;
909 goto exit;
910 }
911#endif
912
913 reop_s->fd = glfs_open(reop_s->glfs, gconf->path, open_flags);
914 if (reop_s->fd == NULL) {
915
916 ret = -errno;
917 goto exit;
918 }
919
920exit:
921
922 qapi_free_BlockdevOptionsGluster(gconf);
923 return ret;
924}
925
926static void qemu_gluster_reopen_commit(BDRVReopenState *state)
927{
928 BDRVGlusterReopenState *reop_s = state->opaque;
929 BDRVGlusterState *s = state->bs->opaque;
930
931
932
933 if (s->fd) {
934 glfs_close(s->fd);
935 }
936
937 glfs_clear_preopened(s->glfs);
938
939
940 s->fd = reop_s->fd;
941 s->glfs = reop_s->glfs;
942
943 g_free(state->opaque);
944 state->opaque = NULL;
945
946 return;
947}
948
949
950static void qemu_gluster_reopen_abort(BDRVReopenState *state)
951{
952 BDRVGlusterReopenState *reop_s = state->opaque;
953
954 if (reop_s == NULL) {
955 return;
956 }
957
958 if (reop_s->fd) {
959 glfs_close(reop_s->fd);
960 }
961
962 glfs_clear_preopened(reop_s->glfs);
963
964 g_free(state->opaque);
965 state->opaque = NULL;
966
967 return;
968}
969
970#ifdef CONFIG_GLUSTERFS_ZEROFILL
971static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs,
972 int64_t offset,
973 int size,
974 BdrvRequestFlags flags)
975{
976 int ret;
977 GlusterAIOCB acb;
978 BDRVGlusterState *s = bs->opaque;
979
980 acb.size = size;
981 acb.ret = 0;
982 acb.coroutine = qemu_coroutine_self();
983 acb.aio_context = bdrv_get_aio_context(bs);
984
985 ret = glfs_zerofill_async(s->fd, offset, size, gluster_finish_aiocb, &acb);
986 if (ret < 0) {
987 return -errno;
988 }
989
990 qemu_coroutine_yield();
991 return acb.ret;
992}
993#endif
994
995static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset,
996 PreallocMode prealloc, Error **errp)
997{
998 int64_t current_length;
999
1000 current_length = glfs_lseek(fd, 0, SEEK_END);
1001 if (current_length < 0) {
1002 error_setg_errno(errp, errno, "Failed to determine current size");
1003 return -errno;
1004 }
1005
1006 if (current_length > offset && prealloc != PREALLOC_MODE_OFF) {
1007 error_setg(errp, "Cannot use preallocation for shrinking files");
1008 return -ENOTSUP;
1009 }
1010
1011 if (current_length == offset) {
1012 return 0;
1013 }
1014
1015 switch (prealloc) {
1016#ifdef CONFIG_GLUSTERFS_FALLOCATE
1017 case PREALLOC_MODE_FALLOC:
1018 if (glfs_fallocate(fd, 0, current_length, offset - current_length)) {
1019 error_setg_errno(errp, errno, "Could not preallocate data");
1020 return -errno;
1021 }
1022 break;
1023#endif
1024#ifdef CONFIG_GLUSTERFS_ZEROFILL
1025 case PREALLOC_MODE_FULL:
1026 if (glfs_ftruncate(fd, offset)) {
1027 error_setg_errno(errp, errno, "Could not resize file");
1028 return -errno;
1029 }
1030 if (glfs_zerofill(fd, current_length, offset - current_length)) {
1031 error_setg_errno(errp, errno, "Could not zerofill the new area");
1032 return -errno;
1033 }
1034 break;
1035#endif
1036 case PREALLOC_MODE_OFF:
1037 if (glfs_ftruncate(fd, offset)) {
1038 error_setg_errno(errp, errno, "Could not resize file");
1039 return -errno;
1040 }
1041 break;
1042 default:
1043 error_setg(errp, "Unsupported preallocation mode: %s",
1044 PreallocMode_str(prealloc));
1045 return -EINVAL;
1046 }
1047
1048 return 0;
1049}
1050
1051static int qemu_gluster_co_create(BlockdevCreateOptions *options,
1052 Error **errp)
1053{
1054 BlockdevCreateOptionsGluster *opts = &options->u.gluster;
1055 struct glfs *glfs;
1056 struct glfs_fd *fd = NULL;
1057 int ret = 0;
1058
1059 assert(options->driver == BLOCKDEV_DRIVER_GLUSTER);
1060
1061 glfs = qemu_gluster_glfs_init(opts->location, errp);
1062 if (!glfs) {
1063 ret = -errno;
1064 goto out;
1065 }
1066
1067 fd = glfs_creat(glfs, opts->location->path,
1068 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
1069 if (!fd) {
1070 ret = -errno;
1071 goto out;
1072 }
1073
1074 ret = qemu_gluster_do_truncate(fd, opts->size, opts->preallocation, errp);
1075
1076out:
1077 if (fd) {
1078 if (glfs_close(fd) != 0 && ret == 0) {
1079 ret = -errno;
1080 }
1081 }
1082 glfs_clear_preopened(glfs);
1083 return ret;
1084}
1085
1086static int coroutine_fn qemu_gluster_co_create_opts(const char *filename,
1087 QemuOpts *opts,
1088 Error **errp)
1089{
1090 BlockdevCreateOptions *options;
1091 BlockdevCreateOptionsGluster *gopts;
1092 BlockdevOptionsGluster *gconf;
1093 char *tmp = NULL;
1094 Error *local_err = NULL;
1095 int ret;
1096
1097 options = g_new0(BlockdevCreateOptions, 1);
1098 options->driver = BLOCKDEV_DRIVER_GLUSTER;
1099 gopts = &options->u.gluster;
1100
1101 gconf = g_new0(BlockdevOptionsGluster, 1);
1102 gopts->location = gconf;
1103
1104 gopts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
1105 BDRV_SECTOR_SIZE);
1106
1107 tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
1108 gopts->preallocation = qapi_enum_parse(&PreallocMode_lookup, tmp,
1109 PREALLOC_MODE_OFF, &local_err);
1110 g_free(tmp);
1111 if (local_err) {
1112 error_propagate(errp, local_err);
1113 ret = -EINVAL;
1114 goto fail;
1115 }
1116
1117 gconf->debug = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG,
1118 GLUSTER_DEBUG_DEFAULT);
1119 if (gconf->debug < 0) {
1120 gconf->debug = 0;
1121 } else if (gconf->debug > GLUSTER_DEBUG_MAX) {
1122 gconf->debug = GLUSTER_DEBUG_MAX;
1123 }
1124 gconf->has_debug = true;
1125
1126 gconf->logfile = qemu_opt_get_del(opts, GLUSTER_OPT_LOGFILE);
1127 if (!gconf->logfile) {
1128 gconf->logfile = g_strdup(GLUSTER_LOGFILE_DEFAULT);
1129 }
1130 gconf->has_logfile = true;
1131
1132 ret = qemu_gluster_parse(gconf, filename, NULL, errp);
1133 if (ret < 0) {
1134 goto fail;
1135 }
1136
1137 ret = qemu_gluster_co_create(options, errp);
1138 if (ret < 0) {
1139 goto fail;
1140 }
1141
1142 ret = 0;
1143fail:
1144 qapi_free_BlockdevCreateOptions(options);
1145 return ret;
1146}
1147
1148static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
1149 int64_t sector_num, int nb_sectors,
1150 QEMUIOVector *qiov, int write)
1151{
1152 int ret;
1153 GlusterAIOCB acb;
1154 BDRVGlusterState *s = bs->opaque;
1155 size_t size = nb_sectors * BDRV_SECTOR_SIZE;
1156 off_t offset = sector_num * BDRV_SECTOR_SIZE;
1157
1158 acb.size = size;
1159 acb.ret = 0;
1160 acb.coroutine = qemu_coroutine_self();
1161 acb.aio_context = bdrv_get_aio_context(bs);
1162
1163 if (write) {
1164 ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
1165 gluster_finish_aiocb, &acb);
1166 } else {
1167 ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0,
1168 gluster_finish_aiocb, &acb);
1169 }
1170
1171 if (ret < 0) {
1172 return -errno;
1173 }
1174
1175 qemu_coroutine_yield();
1176 return acb.ret;
1177}
1178
1179static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset,
1180 PreallocMode prealloc, Error **errp)
1181{
1182 BDRVGlusterState *s = bs->opaque;
1183 return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp);
1184}
1185
1186static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,
1187 int64_t sector_num,
1188 int nb_sectors,
1189 QEMUIOVector *qiov)
1190{
1191 return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 0);
1192}
1193
1194static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs,
1195 int64_t sector_num,
1196 int nb_sectors,
1197 QEMUIOVector *qiov)
1198{
1199 return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1);
1200}
1201
1202static void qemu_gluster_close(BlockDriverState *bs)
1203{
1204 BDRVGlusterState *s = bs->opaque;
1205
1206 g_free(s->logfile);
1207 if (s->fd) {
1208 glfs_close(s->fd);
1209 s->fd = NULL;
1210 }
1211 glfs_clear_preopened(s->glfs);
1212}
1213
1214static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs)
1215{
1216 int ret;
1217 GlusterAIOCB acb;
1218 BDRVGlusterState *s = bs->opaque;
1219
1220 acb.size = 0;
1221 acb.ret = 0;
1222 acb.coroutine = qemu_coroutine_self();
1223 acb.aio_context = bdrv_get_aio_context(bs);
1224
1225 ret = glfs_fsync_async(s->fd, gluster_finish_aiocb, &acb);
1226 if (ret < 0) {
1227 ret = -errno;
1228 goto error;
1229 }
1230
1231 qemu_coroutine_yield();
1232 if (acb.ret < 0) {
1233 ret = acb.ret;
1234 goto error;
1235 }
1236
1237 return acb.ret;
1238
1239error:
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253 qemu_gluster_close(bs);
1254 bs->drv = NULL;
1255 return ret;
1256}
1257
1258#ifdef CONFIG_GLUSTERFS_DISCARD
1259static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs,
1260 int64_t offset, int size)
1261{
1262 int ret;
1263 GlusterAIOCB acb;
1264 BDRVGlusterState *s = bs->opaque;
1265
1266 acb.size = 0;
1267 acb.ret = 0;
1268 acb.coroutine = qemu_coroutine_self();
1269 acb.aio_context = bdrv_get_aio_context(bs);
1270
1271 ret = glfs_discard_async(s->fd, offset, size, gluster_finish_aiocb, &acb);
1272 if (ret < 0) {
1273 return -errno;
1274 }
1275
1276 qemu_coroutine_yield();
1277 return acb.ret;
1278}
1279#endif
1280
1281static int64_t qemu_gluster_getlength(BlockDriverState *bs)
1282{
1283 BDRVGlusterState *s = bs->opaque;
1284 int64_t ret;
1285
1286 ret = glfs_lseek(s->fd, 0, SEEK_END);
1287 if (ret < 0) {
1288 return -errno;
1289 } else {
1290 return ret;
1291 }
1292}
1293
1294static int64_t qemu_gluster_allocated_file_size(BlockDriverState *bs)
1295{
1296 BDRVGlusterState *s = bs->opaque;
1297 struct stat st;
1298 int ret;
1299
1300 ret = glfs_fstat(s->fd, &st);
1301 if (ret < 0) {
1302 return -errno;
1303 } else {
1304 return st.st_blocks * 512;
1305 }
1306}
1307
1308static int qemu_gluster_has_zero_init(BlockDriverState *bs)
1309{
1310
1311 return 0;
1312}
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326static int find_allocation(BlockDriverState *bs, off_t start,
1327 off_t *data, off_t *hole)
1328{
1329 BDRVGlusterState *s = bs->opaque;
1330
1331 if (!s->supports_seek_data) {
1332 goto exit;
1333 }
1334
1335#if defined SEEK_HOLE && defined SEEK_DATA
1336 off_t offs;
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349 offs = glfs_lseek(s->fd, start, SEEK_DATA);
1350 if (offs < 0) {
1351 return -errno;
1352 }
1353
1354 if (offs < start) {
1355
1356
1357
1358
1359 return -EIO;
1360 }
1361
1362 if (offs > start) {
1363
1364 *hole = start;
1365 *data = offs;
1366 return 0;
1367 }
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388 offs = glfs_lseek(s->fd, start, SEEK_HOLE);
1389 if (offs < 0) {
1390 return -errno;
1391 }
1392
1393 if (offs < start) {
1394
1395
1396
1397
1398 return -EIO;
1399 }
1400
1401 if (offs > start) {
1402
1403
1404
1405
1406
1407
1408 *data = start;
1409 *hole = offs;
1410 return 0;
1411 }
1412
1413
1414 return -EBUSY;
1415#endif
1416
1417exit:
1418 return -ENOTSUP;
1419}
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs,
1435 bool want_zero,
1436 int64_t offset,
1437 int64_t bytes,
1438 int64_t *pnum,
1439 int64_t *map,
1440 BlockDriverState **file)
1441{
1442 BDRVGlusterState *s = bs->opaque;
1443 off_t data = 0, hole = 0;
1444 int ret = -EINVAL;
1445
1446 if (!s->fd) {
1447 return ret;
1448 }
1449
1450 if (!want_zero) {
1451 *pnum = bytes;
1452 *map = offset;
1453 *file = bs;
1454 return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
1455 }
1456
1457 ret = find_allocation(bs, offset, &data, &hole);
1458 if (ret == -ENXIO) {
1459
1460 *pnum = bytes;
1461 ret = BDRV_BLOCK_ZERO;
1462 } else if (ret < 0) {
1463
1464 *pnum = bytes;
1465 ret = BDRV_BLOCK_DATA;
1466 } else if (data == offset) {
1467
1468
1469 *pnum = MIN(bytes, hole - offset);
1470 ret = BDRV_BLOCK_DATA;
1471 } else {
1472
1473 assert(hole == offset);
1474 *pnum = MIN(bytes, data - offset);
1475 ret = BDRV_BLOCK_ZERO;
1476 }
1477
1478 *map = offset;
1479 *file = bs;
1480
1481 return ret | BDRV_BLOCK_OFFSET_VALID;
1482}
1483
1484
1485static BlockDriver bdrv_gluster = {
1486 .format_name = "gluster",
1487 .protocol_name = "gluster",
1488 .instance_size = sizeof(BDRVGlusterState),
1489 .bdrv_needs_filename = false,
1490 .bdrv_file_open = qemu_gluster_open,
1491 .bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
1492 .bdrv_reopen_commit = qemu_gluster_reopen_commit,
1493 .bdrv_reopen_abort = qemu_gluster_reopen_abort,
1494 .bdrv_close = qemu_gluster_close,
1495 .bdrv_co_create = qemu_gluster_co_create,
1496 .bdrv_co_create_opts = qemu_gluster_co_create_opts,
1497 .bdrv_getlength = qemu_gluster_getlength,
1498 .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
1499 .bdrv_truncate = qemu_gluster_truncate,
1500 .bdrv_co_readv = qemu_gluster_co_readv,
1501 .bdrv_co_writev = qemu_gluster_co_writev,
1502 .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
1503 .bdrv_has_zero_init = qemu_gluster_has_zero_init,
1504#ifdef CONFIG_GLUSTERFS_DISCARD
1505 .bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
1506#endif
1507#ifdef CONFIG_GLUSTERFS_ZEROFILL
1508 .bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
1509#endif
1510 .bdrv_co_block_status = qemu_gluster_co_block_status,
1511 .create_opts = &qemu_gluster_create_opts,
1512};
1513
1514static BlockDriver bdrv_gluster_tcp = {
1515 .format_name = "gluster",
1516 .protocol_name = "gluster+tcp",
1517 .instance_size = sizeof(BDRVGlusterState),
1518 .bdrv_needs_filename = false,
1519 .bdrv_file_open = qemu_gluster_open,
1520 .bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
1521 .bdrv_reopen_commit = qemu_gluster_reopen_commit,
1522 .bdrv_reopen_abort = qemu_gluster_reopen_abort,
1523 .bdrv_close = qemu_gluster_close,
1524 .bdrv_co_create = qemu_gluster_co_create,
1525 .bdrv_co_create_opts = qemu_gluster_co_create_opts,
1526 .bdrv_getlength = qemu_gluster_getlength,
1527 .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
1528 .bdrv_truncate = qemu_gluster_truncate,
1529 .bdrv_co_readv = qemu_gluster_co_readv,
1530 .bdrv_co_writev = qemu_gluster_co_writev,
1531 .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
1532 .bdrv_has_zero_init = qemu_gluster_has_zero_init,
1533#ifdef CONFIG_GLUSTERFS_DISCARD
1534 .bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
1535#endif
1536#ifdef CONFIG_GLUSTERFS_ZEROFILL
1537 .bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
1538#endif
1539 .bdrv_co_block_status = qemu_gluster_co_block_status,
1540 .create_opts = &qemu_gluster_create_opts,
1541};
1542
1543static BlockDriver bdrv_gluster_unix = {
1544 .format_name = "gluster",
1545 .protocol_name = "gluster+unix",
1546 .instance_size = sizeof(BDRVGlusterState),
1547 .bdrv_needs_filename = true,
1548 .bdrv_file_open = qemu_gluster_open,
1549 .bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
1550 .bdrv_reopen_commit = qemu_gluster_reopen_commit,
1551 .bdrv_reopen_abort = qemu_gluster_reopen_abort,
1552 .bdrv_close = qemu_gluster_close,
1553 .bdrv_co_create = qemu_gluster_co_create,
1554 .bdrv_co_create_opts = qemu_gluster_co_create_opts,
1555 .bdrv_getlength = qemu_gluster_getlength,
1556 .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
1557 .bdrv_truncate = qemu_gluster_truncate,
1558 .bdrv_co_readv = qemu_gluster_co_readv,
1559 .bdrv_co_writev = qemu_gluster_co_writev,
1560 .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
1561 .bdrv_has_zero_init = qemu_gluster_has_zero_init,
1562#ifdef CONFIG_GLUSTERFS_DISCARD
1563 .bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
1564#endif
1565#ifdef CONFIG_GLUSTERFS_ZEROFILL
1566 .bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
1567#endif
1568 .bdrv_co_block_status = qemu_gluster_co_block_status,
1569 .create_opts = &qemu_gluster_create_opts,
1570};
1571
1572
1573
1574
1575
1576
1577
1578static BlockDriver bdrv_gluster_rdma = {
1579 .format_name = "gluster",
1580 .protocol_name = "gluster+rdma",
1581 .instance_size = sizeof(BDRVGlusterState),
1582 .bdrv_needs_filename = true,
1583 .bdrv_file_open = qemu_gluster_open,
1584 .bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
1585 .bdrv_reopen_commit = qemu_gluster_reopen_commit,
1586 .bdrv_reopen_abort = qemu_gluster_reopen_abort,
1587 .bdrv_close = qemu_gluster_close,
1588 .bdrv_co_create = qemu_gluster_co_create,
1589 .bdrv_co_create_opts = qemu_gluster_co_create_opts,
1590 .bdrv_getlength = qemu_gluster_getlength,
1591 .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
1592 .bdrv_truncate = qemu_gluster_truncate,
1593 .bdrv_co_readv = qemu_gluster_co_readv,
1594 .bdrv_co_writev = qemu_gluster_co_writev,
1595 .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
1596 .bdrv_has_zero_init = qemu_gluster_has_zero_init,
1597#ifdef CONFIG_GLUSTERFS_DISCARD
1598 .bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
1599#endif
1600#ifdef CONFIG_GLUSTERFS_ZEROFILL
1601 .bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
1602#endif
1603 .bdrv_co_block_status = qemu_gluster_co_block_status,
1604 .create_opts = &qemu_gluster_create_opts,
1605};
1606
1607static void bdrv_gluster_init(void)
1608{
1609 bdrv_register(&bdrv_gluster_rdma);
1610 bdrv_register(&bdrv_gluster_unix);
1611 bdrv_register(&bdrv_gluster_tcp);
1612 bdrv_register(&bdrv_gluster);
1613}
1614
1615block_init(bdrv_gluster_init);
1616