1 /*
2  * Copyright (C) 2016 FUJITSU LIMITED
3  * Author: Wen Congyang <wency@cn.fujitsu.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published
7  * by the Free Software Foundation; version 2.1 only. with the special
8  * exception on linking described in file LICENSE.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  */
15 
16 #include "libxl_osdeps.h" /* must come before any other headers */
17 
18 #include "libxl_internal.h"
19 
20 /* ========== init() and cleanup() ========== */
21 
init_subkind_qdisk(libxl__checkpoint_devices_state * cds)22 int init_subkind_qdisk(libxl__checkpoint_devices_state *cds)
23 {
24     /*
25      * We don't know if we use qemu block replication, so
26      * we cannot start block replication here.
27      */
28     return 0;
29 }
30 
cleanup_subkind_qdisk(libxl__checkpoint_devices_state * cds)31 void cleanup_subkind_qdisk(libxl__checkpoint_devices_state *cds)
32 {
33 }
34 
35 /* ========== setup() and teardown() ========== */
36 
colo_qdisk_setup(libxl__egc * egc,libxl__checkpoint_device * dev,bool primary)37 static void colo_qdisk_setup(libxl__egc *egc, libxl__checkpoint_device *dev,
38                              bool primary)
39 {
40     const libxl_device_disk *disk = dev->backend_dev;
41     int ret, rc = 0;
42     libxl__colo_qdisk *colo_qdisk = NULL;
43     char port[32];
44 
45     /* Convenience aliases */
46     libxl__checkpoint_devices_state *const cds = dev->cds;
47     const char *host = disk->colo_host;
48     const char *export_name = disk->colo_export;
49     const int domid = cds->domid;
50 
51     STATE_AO_GC(dev->cds->ao);
52 
53     if (disk->backend != LIBXL_DISK_BACKEND_QDISK ||
54         !libxl_defbool_val(disk->colo_enable) ||
55         !host || !export_name || (disk->colo_port <= 0) ||
56         !disk->active_disk || !disk->hidden_disk) {
57         rc = ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH;
58         goto out;
59     }
60 
61     dev->matched = true;
62 
63     GCNEW(colo_qdisk);
64     dev->concrete_data = colo_qdisk;
65 
66     if (primary) {
67         libxl__colo_save_state *css = cds->concrete_data;
68 
69         css->qdisk_used = true;
70         /* NBD server is not ready, so we cannot start block replication now */
71         goto out;
72     } else {
73         libxl__colo_restore_state *crs = cds->concrete_data;
74         sprintf(port, "%d", disk->colo_port);
75 
76         if (!crs->qdisk_used) {
77             /* start nbd server */
78             ret = libxl__qmp_nbd_server_start(gc, domid, host, port);
79             if (ret) {
80                 rc = ERROR_FAIL;
81                 goto out;
82             }
83             crs->host = host;
84             crs->port = port;
85         } else {
86             if (strcmp(crs->host, host) || strcmp(crs->port, port)) {
87                 LOGD(ERROR, domid, "The host and port of all disks must be the same");
88                 rc = ERROR_FAIL;
89                 goto out;
90             }
91         }
92 
93         crs->qdisk_used = true;
94 
95         ret = libxl__qmp_nbd_server_add(gc, domid, export_name);
96         if (ret)
97             rc = ERROR_FAIL;
98 
99         colo_qdisk->setuped = true;
100     }
101 
102 out:
103     dev->aodev.rc = rc;
104     dev->aodev.callback(egc, &dev->aodev);
105 }
106 
colo_qdisk_teardown(libxl__egc * egc,libxl__checkpoint_device * dev,bool primary)107 static void colo_qdisk_teardown(libxl__egc *egc, libxl__checkpoint_device *dev,
108                                 bool primary)
109 {
110     int ret, rc = 0;
111     const libxl__colo_qdisk *colo_qdisk = dev->concrete_data;
112     const libxl_device_disk *disk = dev->backend_dev;
113 
114     /* Convenience aliases */
115     libxl__checkpoint_devices_state *const cds = dev->cds;
116     const int domid = cds->domid;
117     const char *export_name = disk->colo_export;
118 
119     EGC_GC;
120 
121     if (primary) {
122         if (!colo_qdisk->setuped)
123             goto out;
124 
125         /*
126          * There is no way to get the child name, but we know it is children.1
127          */
128         ret = libxl__qmp_x_blockdev_change(gc, domid, export_name,
129                                            "children.1", NULL);
130         if (ret)
131             rc = ERROR_FAIL;
132     } else {
133         libxl__colo_restore_state *crs = cds->concrete_data;
134 
135         if (crs->qdisk_used) {
136             ret = libxl__qmp_nbd_server_stop(gc, domid);
137             if (ret)
138                 rc = ERROR_FAIL;
139         }
140     }
141 
142 out:
143     dev->aodev.rc = rc;
144     dev->aodev.callback(egc, &dev->aodev);
145 }
146 
147 /* ========== checkpointing APIs ========== */
148 
colo_qdisk_save_preresume(libxl__egc * egc,libxl__checkpoint_device * dev)149 static void colo_qdisk_save_preresume(libxl__egc *egc,
150                                       libxl__checkpoint_device *dev)
151 {
152     libxl__colo_qdisk *colo_qdisk = dev->concrete_data;
153     const libxl_device_disk *disk = dev->backend_dev;
154     int ret, rc = 0;
155     char *node = NULL;
156     char *cmd = NULL;
157 
158     /* Convenience aliases */
159     const int domid = dev->cds->domid;
160     const char *host = disk->colo_host;
161     int port = disk->colo_port;
162     const char *export_name = disk->colo_export;
163 
164     EGC_GC;
165 
166     if (colo_qdisk->setuped)
167         goto out;
168 
169     /* qmp command doesn't support the driver "nbd" */
170     node = GCSPRINTF("colo_node%d",
171                      libxl__device_disk_dev_number(disk->vdev, NULL, NULL));
172     cmd = GCSPRINTF("drive_add -n buddy driver=replication,mode=primary,"
173                     "file.driver=nbd,file.host=%s,file.port=%d,"
174                     "file.export=%s,node-name=%s",
175                     host, port, export_name, node);
176     ret = libxl__qmp_hmp(gc, domid, cmd, NULL);
177     if (ret)
178         rc = ERROR_FAIL;
179 
180     ret = libxl__qmp_x_blockdev_change(gc, domid, export_name, NULL, node);
181     if (ret)
182         rc = ERROR_FAIL;
183 
184     colo_qdisk->setuped = true;
185 
186 out:
187     dev->aodev.rc = rc;
188     dev->aodev.callback(egc, &dev->aodev);
189 }
190 
191 /* ======== primary ======== */
192 
colo_qdisk_save_setup(libxl__egc * egc,libxl__checkpoint_device * dev)193 static void colo_qdisk_save_setup(libxl__egc *egc,
194                                   libxl__checkpoint_device *dev)
195 {
196     colo_qdisk_setup(egc, dev, true);
197 }
198 
colo_qdisk_save_teardown(libxl__egc * egc,libxl__checkpoint_device * dev)199 static void colo_qdisk_save_teardown(libxl__egc *egc,
200                                    libxl__checkpoint_device *dev)
201 {
202     colo_qdisk_teardown(egc, dev, true);
203 }
204 
205 const libxl__checkpoint_device_instance_ops colo_save_device_qdisk = {
206     .kind = LIBXL__DEVICE_KIND_VBD,
207     .setup = colo_qdisk_save_setup,
208     .teardown = colo_qdisk_save_teardown,
209     .preresume = colo_qdisk_save_preresume,
210 };
211 
212 /* ======== secondary ======== */
213 
colo_qdisk_restore_setup(libxl__egc * egc,libxl__checkpoint_device * dev)214 static void colo_qdisk_restore_setup(libxl__egc *egc,
215                                      libxl__checkpoint_device *dev)
216 {
217     colo_qdisk_setup(egc, dev, false);
218 }
219 
colo_qdisk_restore_teardown(libxl__egc * egc,libxl__checkpoint_device * dev)220 static void colo_qdisk_restore_teardown(libxl__egc *egc,
221                                       libxl__checkpoint_device *dev)
222 {
223     colo_qdisk_teardown(egc, dev, false);
224 }
225 
226 const libxl__checkpoint_device_instance_ops colo_restore_device_qdisk = {
227     .kind = LIBXL__DEVICE_KIND_VBD,
228     .setup = colo_qdisk_restore_setup,
229     .teardown = colo_qdisk_restore_teardown,
230 };
231