]> Git Repo - qemu.git/blob - block/vxhs.c
Merge remote-tracking branch 'remotes/dgilbert/tags/pull-migration-20180615a' into...
[qemu.git] / block / vxhs.c
1 /*
2  * QEMU Block driver for Veritas HyperScale (VxHS)
3  *
4  * Copyright (c) 2017 Veritas Technologies LLC.
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7  * See the COPYING file in the top-level directory.
8  *
9  */
10
11 #include "qemu/osdep.h"
12 #include <qnio/qnio_api.h>
13 #include <sys/param.h>
14 #include "block/block_int.h"
15 #include "block/qdict.h"
16 #include "qapi/qmp/qerror.h"
17 #include "qapi/qmp/qdict.h"
18 #include "qapi/qmp/qstring.h"
19 #include "trace.h"
20 #include "qemu/uri.h"
21 #include "qapi/error.h"
22 #include "qemu/uuid.h"
23 #include "crypto/tlscredsx509.h"
24
25 #define VXHS_OPT_FILENAME           "filename"
26 #define VXHS_OPT_VDISK_ID           "vdisk-id"
27 #define VXHS_OPT_SERVER             "server"
28 #define VXHS_OPT_HOST               "host"
29 #define VXHS_OPT_PORT               "port"
30
31 /* Only accessed under QEMU global mutex */
32 static uint32_t vxhs_ref;
33
34 typedef enum {
35     VDISK_AIO_READ,
36     VDISK_AIO_WRITE,
37 } VDISKAIOCmd;
38
39 /*
40  * HyperScale AIO callbacks structure
41  */
42 typedef struct VXHSAIOCB {
43     BlockAIOCB common;
44     int err;
45 } VXHSAIOCB;
46
47 typedef struct VXHSvDiskHostsInfo {
48     void *dev_handle; /* Device handle */
49     char *host; /* Host name or IP */
50     int port; /* Host's port number */
51 } VXHSvDiskHostsInfo;
52
53 /*
54  * Structure per vDisk maintained for state
55  */
56 typedef struct BDRVVXHSState {
57     VXHSvDiskHostsInfo vdisk_hostinfo; /* Per host info */
58     char *vdisk_guid;
59     char *tlscredsid; /* tlscredsid */
60 } BDRVVXHSState;
61
62 static void vxhs_complete_aio_bh(void *opaque)
63 {
64     VXHSAIOCB *acb = opaque;
65     BlockCompletionFunc *cb = acb->common.cb;
66     void *cb_opaque = acb->common.opaque;
67     int ret = 0;
68
69     if (acb->err != 0) {
70         trace_vxhs_complete_aio(acb, acb->err);
71         ret = (-EIO);
72     }
73
74     qemu_aio_unref(acb);
75     cb(cb_opaque, ret);
76 }
77
78 /*
79  * Called from a libqnio thread
80  */
81 static void vxhs_iio_callback(void *ctx, uint32_t opcode, uint32_t error)
82 {
83     VXHSAIOCB *acb = NULL;
84
85     switch (opcode) {
86     case IRP_READ_REQUEST:
87     case IRP_WRITE_REQUEST:
88
89         /*
90          * ctx is VXHSAIOCB*
91          * ctx is NULL if error is QNIOERROR_CHANNEL_HUP
92          */
93         if (ctx) {
94             acb = ctx;
95         } else {
96             trace_vxhs_iio_callback(error);
97             goto out;
98         }
99
100         if (error) {
101             if (!acb->err) {
102                 acb->err = error;
103             }
104             trace_vxhs_iio_callback(error);
105         }
106
107         aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs),
108                                 vxhs_complete_aio_bh, acb);
109         break;
110
111     default:
112         if (error == QNIOERROR_HUP) {
113             /*
114              * Channel failed, spontaneous notification,
115              * not in response to I/O
116              */
117             trace_vxhs_iio_callback_chnfail(error, errno);
118         } else {
119             trace_vxhs_iio_callback_unknwn(opcode, error);
120         }
121         break;
122     }
123 out:
124     return;
125 }
126
127 static QemuOptsList runtime_opts = {
128     .name = "vxhs",
129     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
130     .desc = {
131         {
132             .name = VXHS_OPT_FILENAME,
133             .type = QEMU_OPT_STRING,
134             .help = "URI to the Veritas HyperScale image",
135         },
136         {
137             .name = VXHS_OPT_VDISK_ID,
138             .type = QEMU_OPT_STRING,
139             .help = "UUID of the VxHS vdisk",
140         },
141         {
142             .name = "tls-creds",
143             .type = QEMU_OPT_STRING,
144             .help = "ID of the TLS/SSL credentials to use",
145         },
146         { /* end of list */ }
147     },
148 };
149
150 static QemuOptsList runtime_tcp_opts = {
151     .name = "vxhs_tcp",
152     .head = QTAILQ_HEAD_INITIALIZER(runtime_tcp_opts.head),
153     .desc = {
154         {
155             .name = VXHS_OPT_HOST,
156             .type = QEMU_OPT_STRING,
157             .help = "host address (ipv4 addresses)",
158         },
159         {
160             .name = VXHS_OPT_PORT,
161             .type = QEMU_OPT_NUMBER,
162             .help = "port number on which VxHSD is listening (default 9999)",
163             .def_value_str = "9999"
164         },
165         { /* end of list */ }
166     },
167 };
168
169 /*
170  * Parse incoming URI and populate *options with the host
171  * and device information
172  */
173 static int vxhs_parse_uri(const char *filename, QDict *options)
174 {
175     URI *uri = NULL;
176     char *port;
177     int ret = 0;
178
179     trace_vxhs_parse_uri_filename(filename);
180     uri = uri_parse(filename);
181     if (!uri || !uri->server || !uri->path) {
182         uri_free(uri);
183         return -EINVAL;
184     }
185
186     qdict_put_str(options, VXHS_OPT_SERVER ".host", uri->server);
187
188     if (uri->port) {
189         port = g_strdup_printf("%d", uri->port);
190         qdict_put_str(options, VXHS_OPT_SERVER ".port", port);
191         g_free(port);
192     }
193
194     qdict_put_str(options, "vdisk-id", uri->path);
195
196     trace_vxhs_parse_uri_hostinfo(uri->server, uri->port);
197     uri_free(uri);
198
199     return ret;
200 }
201
202 static void vxhs_parse_filename(const char *filename, QDict *options,
203                                 Error **errp)
204 {
205     if (qdict_haskey(options, "vdisk-id") || qdict_haskey(options, "server")) {
206         error_setg(errp, "vdisk-id/server and a file name may not be specified "
207                          "at the same time");
208         return;
209     }
210
211     if (strstr(filename, "://")) {
212         int ret = vxhs_parse_uri(filename, options);
213         if (ret < 0) {
214             error_setg(errp, "Invalid URI. URI should be of the form "
215                        "  vxhs://<host_ip>:<port>/<vdisk-id>");
216         }
217     }
218 }
219
220 static void vxhs_refresh_limits(BlockDriverState *bs, Error **errp)
221 {
222     /* XXX Does VXHS support AIO on less than 512-byte alignment? */
223     bs->bl.request_alignment = 512;
224 }
225
226 static int vxhs_init_and_ref(void)
227 {
228     if (vxhs_ref++ == 0) {
229         if (iio_init(QNIO_VERSION, vxhs_iio_callback)) {
230             return -ENODEV;
231         }
232     }
233     return 0;
234 }
235
236 static void vxhs_unref(void)
237 {
238     if (--vxhs_ref == 0) {
239         iio_fini();
240     }
241 }
242
243 static void vxhs_get_tls_creds(const char *id, char **cacert,
244                                char **key, char **cert, Error **errp)
245 {
246     Object *obj;
247     QCryptoTLSCreds *creds;
248     QCryptoTLSCredsX509 *creds_x509;
249
250     obj = object_resolve_path_component(
251         object_get_objects_root(), id);
252
253     if (!obj) {
254         error_setg(errp, "No TLS credentials with id '%s'",
255                    id);
256         return;
257     }
258
259     creds_x509 = (QCryptoTLSCredsX509 *)
260         object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS_X509);
261
262     if (!creds_x509) {
263         error_setg(errp, "Object with id '%s' is not TLS credentials",
264                    id);
265         return;
266     }
267
268     creds = &creds_x509->parent_obj;
269
270     if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
271         error_setg(errp,
272                    "Expecting TLS credentials with a client endpoint");
273         return;
274     }
275
276     /*
277      * Get the cacert, client_cert and client_key file names.
278      */
279     if (!creds->dir) {
280         error_setg(errp, "TLS object missing 'dir' property value");
281         return;
282     }
283
284     *cacert = g_strdup_printf("%s/%s", creds->dir,
285                               QCRYPTO_TLS_CREDS_X509_CA_CERT);
286     *cert = g_strdup_printf("%s/%s", creds->dir,
287                             QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
288     *key = g_strdup_printf("%s/%s", creds->dir,
289                            QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
290 }
291
292 static int vxhs_open(BlockDriverState *bs, QDict *options,
293                      int bdrv_flags, Error **errp)
294 {
295     BDRVVXHSState *s = bs->opaque;
296     void *dev_handlep;
297     QDict *backing_options = NULL;
298     QemuOpts *opts = NULL;
299     QemuOpts *tcp_opts = NULL;
300     char *of_vsa_addr = NULL;
301     Error *local_err = NULL;
302     const char *vdisk_id_opt;
303     const char *server_host_opt;
304     int ret = 0;
305     char *cacert = NULL;
306     char *client_key = NULL;
307     char *client_cert = NULL;
308
309     ret = vxhs_init_and_ref();
310     if (ret < 0) {
311         ret = -EINVAL;
312         goto out;
313     }
314
315     /* Create opts info from runtime_opts and runtime_tcp_opts list */
316     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
317     tcp_opts = qemu_opts_create(&runtime_tcp_opts, NULL, 0, &error_abort);
318
319     qemu_opts_absorb_qdict(opts, options, &local_err);
320     if (local_err) {
321         ret = -EINVAL;
322         goto out;
323     }
324
325     /* vdisk-id is the disk UUID */
326     vdisk_id_opt = qemu_opt_get(opts, VXHS_OPT_VDISK_ID);
327     if (!vdisk_id_opt) {
328         error_setg(&local_err, QERR_MISSING_PARAMETER, VXHS_OPT_VDISK_ID);
329         ret = -EINVAL;
330         goto out;
331     }
332
333     /* vdisk-id may contain a leading '/' */
334     if (strlen(vdisk_id_opt) > UUID_FMT_LEN + 1) {
335         error_setg(&local_err, "vdisk-id cannot be more than %d characters",
336                    UUID_FMT_LEN);
337         ret = -EINVAL;
338         goto out;
339     }
340
341     s->vdisk_guid = g_strdup(vdisk_id_opt);
342     trace_vxhs_open_vdiskid(vdisk_id_opt);
343
344     /* get the 'server.' arguments */
345     qdict_extract_subqdict(options, &backing_options, VXHS_OPT_SERVER".");
346
347     qemu_opts_absorb_qdict(tcp_opts, backing_options, &local_err);
348     if (local_err != NULL) {
349         ret = -EINVAL;
350         goto out;
351     }
352
353     server_host_opt = qemu_opt_get(tcp_opts, VXHS_OPT_HOST);
354     if (!server_host_opt) {
355         error_setg(&local_err, QERR_MISSING_PARAMETER,
356                    VXHS_OPT_SERVER"."VXHS_OPT_HOST);
357         ret = -EINVAL;
358         goto out;
359     }
360
361     if (strlen(server_host_opt) > MAXHOSTNAMELEN) {
362         error_setg(&local_err, "server.host cannot be more than %d characters",
363                    MAXHOSTNAMELEN);
364         ret = -EINVAL;
365         goto out;
366     }
367
368     /* check if we got tls-creds via the --object argument */
369     s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds"));
370     if (s->tlscredsid) {
371         vxhs_get_tls_creds(s->tlscredsid, &cacert, &client_key,
372                            &client_cert, &local_err);
373         if (local_err != NULL) {
374             ret = -EINVAL;
375             goto out;
376         }
377         trace_vxhs_get_creds(cacert, client_key, client_cert);
378     }
379
380     s->vdisk_hostinfo.host = g_strdup(server_host_opt);
381     s->vdisk_hostinfo.port = g_ascii_strtoll(qemu_opt_get(tcp_opts,
382                                                           VXHS_OPT_PORT),
383                                                           NULL, 0);
384
385     trace_vxhs_open_hostinfo(s->vdisk_hostinfo.host,
386                              s->vdisk_hostinfo.port);
387
388     of_vsa_addr = g_strdup_printf("of://%s:%d",
389                                   s->vdisk_hostinfo.host,
390                                   s->vdisk_hostinfo.port);
391
392     /*
393      * Open qnio channel to storage agent if not opened before
394      */
395     dev_handlep = iio_open(of_vsa_addr, s->vdisk_guid, 0,
396                            cacert, client_key, client_cert);
397     if (dev_handlep == NULL) {
398         trace_vxhs_open_iio_open(of_vsa_addr);
399         ret = -ENODEV;
400         goto out;
401     }
402     s->vdisk_hostinfo.dev_handle = dev_handlep;
403
404 out:
405     g_free(of_vsa_addr);
406     qobject_unref(backing_options);
407     qemu_opts_del(tcp_opts);
408     qemu_opts_del(opts);
409     g_free(cacert);
410     g_free(client_key);
411     g_free(client_cert);
412
413     if (ret < 0) {
414         vxhs_unref();
415         error_propagate(errp, local_err);
416         g_free(s->vdisk_hostinfo.host);
417         g_free(s->vdisk_guid);
418         g_free(s->tlscredsid);
419         s->vdisk_guid = NULL;
420     }
421
422     return ret;
423 }
424
425 static const AIOCBInfo vxhs_aiocb_info = {
426     .aiocb_size = sizeof(VXHSAIOCB)
427 };
428
429 /*
430  * This allocates QEMU-VXHS callback for each IO
431  * and is passed to QNIO. When QNIO completes the work,
432  * it will be passed back through the callback.
433  */
434 static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, uint64_t offset,
435                                QEMUIOVector *qiov, uint64_t size,
436                                BlockCompletionFunc *cb, void *opaque,
437                                VDISKAIOCmd iodir)
438 {
439     VXHSAIOCB *acb = NULL;
440     BDRVVXHSState *s = bs->opaque;
441     int iio_flags = 0;
442     int ret = 0;
443     void *dev_handle = s->vdisk_hostinfo.dev_handle;
444
445     acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque);
446
447     /*
448      * Initialize VXHSAIOCB.
449      */
450     acb->err = 0;
451
452     iio_flags = IIO_FLAG_ASYNC;
453
454     switch (iodir) {
455     case VDISK_AIO_WRITE:
456             ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov,
457                              offset, size, iio_flags);
458             break;
459     case VDISK_AIO_READ:
460             ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov,
461                             offset, size, iio_flags);
462             break;
463     default:
464             trace_vxhs_aio_rw_invalid(iodir);
465             goto errout;
466     }
467
468     if (ret != 0) {
469         trace_vxhs_aio_rw_ioerr(s->vdisk_guid, iodir, size, offset,
470                                 acb, ret, errno);
471         goto errout;
472     }
473     return &acb->common;
474
475 errout:
476     qemu_aio_unref(acb);
477     return NULL;
478 }
479
480 static BlockAIOCB *vxhs_aio_preadv(BlockDriverState *bs,
481                                    uint64_t offset, uint64_t bytes,
482                                    QEMUIOVector *qiov, int flags,
483                                    BlockCompletionFunc *cb, void *opaque)
484 {
485     return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_READ);
486 }
487
488 static BlockAIOCB *vxhs_aio_pwritev(BlockDriverState *bs,
489                                     uint64_t offset, uint64_t bytes,
490                                     QEMUIOVector *qiov, int flags,
491                                     BlockCompletionFunc *cb, void *opaque)
492 {
493     return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_WRITE);
494 }
495
496 static void vxhs_close(BlockDriverState *bs)
497 {
498     BDRVVXHSState *s = bs->opaque;
499
500     trace_vxhs_close(s->vdisk_guid);
501
502     g_free(s->vdisk_guid);
503     s->vdisk_guid = NULL;
504
505     /*
506      * Close vDisk device
507      */
508     if (s->vdisk_hostinfo.dev_handle) {
509         iio_close(s->vdisk_hostinfo.dev_handle);
510         s->vdisk_hostinfo.dev_handle = NULL;
511     }
512
513     vxhs_unref();
514
515     /*
516      * Free the dynamically allocated host string etc
517      */
518     g_free(s->vdisk_hostinfo.host);
519     g_free(s->tlscredsid);
520     s->tlscredsid = NULL;
521     s->vdisk_hostinfo.host = NULL;
522     s->vdisk_hostinfo.port = 0;
523 }
524
525 static int64_t vxhs_get_vdisk_stat(BDRVVXHSState *s)
526 {
527     int64_t vdisk_size = -1;
528     int ret = 0;
529     void *dev_handle = s->vdisk_hostinfo.dev_handle;
530
531     ret = iio_ioctl(dev_handle, IOR_VDISK_STAT, &vdisk_size, 0);
532     if (ret < 0) {
533         trace_vxhs_get_vdisk_stat_err(s->vdisk_guid, ret, errno);
534         return -EIO;
535     }
536
537     trace_vxhs_get_vdisk_stat(s->vdisk_guid, vdisk_size);
538     return vdisk_size;
539 }
540
541 /*
542  * Returns the size of vDisk in bytes. This is required
543  * by QEMU block upper block layer so that it is visible
544  * to guest.
545  */
546 static int64_t vxhs_getlength(BlockDriverState *bs)
547 {
548     BDRVVXHSState *s = bs->opaque;
549     int64_t vdisk_size;
550
551     vdisk_size = vxhs_get_vdisk_stat(s);
552     if (vdisk_size < 0) {
553         return -EIO;
554     }
555
556     return vdisk_size;
557 }
558
559 static BlockDriver bdrv_vxhs = {
560     .format_name                  = "vxhs",
561     .protocol_name                = "vxhs",
562     .instance_size                = sizeof(BDRVVXHSState),
563     .bdrv_file_open               = vxhs_open,
564     .bdrv_parse_filename          = vxhs_parse_filename,
565     .bdrv_refresh_limits          = vxhs_refresh_limits,
566     .bdrv_close                   = vxhs_close,
567     .bdrv_getlength               = vxhs_getlength,
568     .bdrv_aio_preadv              = vxhs_aio_preadv,
569     .bdrv_aio_pwritev             = vxhs_aio_pwritev,
570 };
571
572 static void bdrv_vxhs_init(void)
573 {
574     bdrv_register(&bdrv_vxhs);
575 }
576
577 block_init(bdrv_vxhs_init);
This page took 0.055538 seconds and 4 git commands to generate.