1 /*
2  * Copyright (C) 2014 FUJITSU LIMITED
3  * Author Lai Jiangshan <laijs@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 /*** drbd implementation ***/
21 const int DRBD_SEND_CHECKPOINT = 20;
22 const int DRBD_WAIT_CHECKPOINT_ACK = 30;
23 
24 typedef struct libxl__remus_drbd_disk {
25     int ctl_fd;
26     int ackwait;
27 } libxl__remus_drbd_disk;
28 
init_subkind_drbd_disk(libxl__checkpoint_devices_state * cds)29 int init_subkind_drbd_disk(libxl__checkpoint_devices_state *cds)
30 {
31     libxl__remus_state *rs = cds->concrete_data;
32     STATE_AO_GC(cds->ao);
33 
34     rs->drbd_probe_script = GCSPRINTF("%s/block-drbd-probe",
35                                       libxl__xen_script_dir_path());
36 
37     return 0;
38 }
39 
cleanup_subkind_drbd_disk(libxl__checkpoint_devices_state * cds)40 void cleanup_subkind_drbd_disk(libxl__checkpoint_devices_state *cds)
41 {
42     return;
43 }
44 
45 /*----- match(), setup() and teardown() -----*/
46 
47 /* callbacks */
48 static void match_async_exec_cb(libxl__egc *egc,
49                                 libxl__async_exec_state *aes,
50                                 int rc, int status);
51 
52 /* implementations */
53 
54 static void match_async_exec(libxl__egc *egc, libxl__checkpoint_device *dev);
55 
drbd_setup(libxl__egc * egc,libxl__checkpoint_device * dev)56 static void drbd_setup(libxl__egc *egc, libxl__checkpoint_device *dev)
57 {
58     STATE_AO_GC(dev->cds->ao);
59 
60     match_async_exec(egc, dev);
61 }
62 
match_async_exec(libxl__egc * egc,libxl__checkpoint_device * dev)63 static void match_async_exec(libxl__egc *egc, libxl__checkpoint_device *dev)
64 {
65     int arraysize, nr = 0, rc;
66     const libxl_device_disk *disk = dev->backend_dev;
67     libxl__async_exec_state *aes = &dev->aodev.aes;
68     libxl__remus_state *rs = dev->cds->concrete_data;
69     STATE_AO_GC(dev->cds->ao);
70 
71     /* setup env & args */
72     arraysize = 1;
73     GCNEW_ARRAY(aes->env, arraysize);
74     aes->env[nr++] = NULL;
75     assert(nr <= arraysize);
76 
77     arraysize = 3;
78     nr = 0;
79     GCNEW_ARRAY(aes->args, arraysize);
80     aes->args[nr++] = rs->drbd_probe_script;
81     aes->args[nr++] = disk->pdev_path;
82     aes->args[nr++] = NULL;
83     assert(nr <= arraysize);
84 
85     aes->ao = dev->cds->ao;
86     aes->what = GCSPRINTF("%s %s", aes->args[0], aes->args[1]);
87     aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000;
88     aes->callback = match_async_exec_cb;
89     aes->stdfds[0] = -1;
90     aes->stdfds[1] = -1;
91     aes->stdfds[2] = -1;
92 
93     rc = libxl__async_exec_start(aes);
94     if (rc)
95         goto out;
96 
97     return;
98 
99 out:
100     dev->aodev.rc = rc;
101     dev->aodev.callback(egc, &dev->aodev);
102 }
103 
match_async_exec_cb(libxl__egc * egc,libxl__async_exec_state * aes,int rc,int status)104 static void match_async_exec_cb(libxl__egc *egc,
105                                 libxl__async_exec_state *aes,
106                                 int rc, int status)
107 {
108     libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes);
109     libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
110     libxl__remus_drbd_disk *drbd_disk;
111     const libxl_device_disk *disk = dev->backend_dev;
112 
113     STATE_AO_GC(aodev->ao);
114 
115     if (rc)
116         goto out;
117 
118     if (status) {
119         rc = ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH;
120         /* BUG: seems to assume that any exit status means `no match' */
121         /* BUG: exit status will have been logged as an error */
122         goto out;
123     }
124 
125     /* ops matched */
126     dev->matched = true;
127 
128     GCNEW(drbd_disk);
129     dev->concrete_data = drbd_disk;
130     drbd_disk->ackwait = 0;
131     drbd_disk->ctl_fd = open(disk->pdev_path, O_RDONLY);
132     if (drbd_disk->ctl_fd < 0) {
133         rc = ERROR_FAIL;
134         goto out;
135     }
136 
137     rc = 0;
138 
139 out:
140     aodev->rc = rc;
141     aodev->callback(egc, aodev);
142 }
143 
drbd_teardown(libxl__egc * egc,libxl__checkpoint_device * dev)144 static void drbd_teardown(libxl__egc *egc, libxl__checkpoint_device *dev)
145 {
146     libxl__remus_drbd_disk *drbd_disk = dev->concrete_data;
147     STATE_AO_GC(dev->cds->ao);
148 
149     close(drbd_disk->ctl_fd);
150     dev->aodev.rc = 0;
151     dev->aodev.callback(egc, &dev->aodev);
152 }
153 
154 /*----- checkpointing APIs -----*/
155 
156 /* callbacks */
157 static void checkpoint_async_call_done(libxl__egc *egc,
158                                        libxl__ev_child *child,
159                                        pid_t pid, int status);
160 
161 /* API implementations */
162 
163 /* this op will not wait and block, so implement as sync op */
drbd_postsuspend(libxl__egc * egc,libxl__checkpoint_device * dev)164 static void drbd_postsuspend(libxl__egc *egc, libxl__checkpoint_device *dev)
165 {
166     STATE_AO_GC(dev->cds->ao);
167 
168     libxl__remus_drbd_disk *rdd = dev->concrete_data;
169 
170     if (!rdd->ackwait) {
171         if (ioctl(rdd->ctl_fd, DRBD_SEND_CHECKPOINT, 0) <= 0)
172             rdd->ackwait = 1;
173     }
174 
175     dev->aodev.rc = 0;
176     dev->aodev.callback(egc, &dev->aodev);
177 }
178 
179 
180 static void drbd_preresume_async(libxl__checkpoint_device *dev);
181 
drbd_preresume(libxl__egc * egc,libxl__checkpoint_device * dev)182 static void drbd_preresume(libxl__egc *egc, libxl__checkpoint_device *dev)
183 {
184     ASYNC_CALL(egc, dev->cds->ao, &dev->aodev.child, dev,
185                drbd_preresume_async,
186                checkpoint_async_call_done);
187 }
188 
drbd_preresume_async(libxl__checkpoint_device * dev)189 static void drbd_preresume_async(libxl__checkpoint_device *dev)
190 {
191     libxl__remus_drbd_disk *rdd = dev->concrete_data;
192     int ackwait = rdd->ackwait;
193 
194     if (ackwait) {
195         ioctl(rdd->ctl_fd, DRBD_WAIT_CHECKPOINT_ACK, 0);
196         ackwait = 0;
197     }
198 
199     _exit(ackwait);
200 }
201 
checkpoint_async_call_done(libxl__egc * egc,libxl__ev_child * child,pid_t pid,int status)202 static void checkpoint_async_call_done(libxl__egc *egc,
203                                        libxl__ev_child *child,
204                                        pid_t pid, int status)
205 {
206     int rc;
207     libxl__ao_device *aodev = CONTAINER_OF(child, *aodev, child);
208     libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev);
209     libxl__remus_drbd_disk *rdd = dev->concrete_data;
210 
211     STATE_AO_GC(aodev->ao);
212 
213     if (!WIFEXITED(status)) {
214         rc = ERROR_FAIL;
215         goto out;
216     }
217 
218     rdd->ackwait = WEXITSTATUS(status);
219     rc = 0;
220 
221 out:
222     aodev->rc = rc;
223     aodev->callback(egc, aodev);
224 }
225 
226 const libxl__checkpoint_device_instance_ops remus_device_drbd_disk = {
227     .kind = LIBXL__DEVICE_KIND_VBD,
228     .setup = drbd_setup,
229     .teardown = drbd_teardown,
230     .postsuspend = drbd_postsuspend,
231     .preresume = drbd_preresume,
232 };
233