]>
Commit | Line | Data |
---|---|---|
7dce4e6f ZC |
1 | /* |
2 | * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO) | |
3 | * (a.k.a. Fault Tolerance or Continuous Replication) | |
4 | * | |
5 | * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. | |
6 | * Copyright (c) 2016 FUJITSU LIMITED | |
7 | * Copyright (c) 2016 Intel Corporation | |
8 | * | |
9 | * Author: Zhang Chen <zhangchen.fnst@cn.fujitsu.com> | |
10 | * | |
11 | * This work is licensed under the terms of the GNU GPL, version 2 or | |
12 | * later. See the COPYING file in the top-level directory. | |
13 | */ | |
14 | ||
15 | #include "qemu/osdep.h" | |
16 | #include "qemu/error-report.h" | |
59509ec1 | 17 | #include "trace.h" |
7dce4e6f ZC |
18 | #include "qemu-common.h" |
19 | #include "qapi/qmp/qerror.h" | |
20 | #include "qapi/error.h" | |
21 | #include "net/net.h" | |
22 | #include "qom/object_interfaces.h" | |
23 | #include "qemu/iov.h" | |
24 | #include "qom/object.h" | |
25 | #include "qemu/typedefs.h" | |
26 | #include "net/queue.h" | |
27 | #include "sysemu/char.h" | |
28 | #include "qemu/sockets.h" | |
29 | #include "qapi-visit.h" | |
59509ec1 | 30 | #include "net/colo.h" |
7dce4e6f ZC |
31 | |
32 | #define TYPE_COLO_COMPARE "colo-compare" | |
33 | #define COLO_COMPARE(obj) \ | |
34 | OBJECT_CHECK(CompareState, (obj), TYPE_COLO_COMPARE) | |
35 | ||
59509ec1 ZC |
36 | /* |
37 | + CompareState ++ | |
38 | | | | |
39 | +---------------+ +---------------+ +---------------+ | |
40 | |conn list +--->conn +--------->conn | | |
41 | +---------------+ +---------------+ +---------------+ | |
42 | | | | | | | | |
43 | +---------------+ +---v----+ +---v----+ +---v----+ +---v----+ | |
44 | |primary | |secondary |primary | |secondary | |
45 | |packet | |packet + |packet | |packet + | |
46 | +--------+ +--------+ +--------+ +--------+ | |
47 | | | | | | |
48 | +---v----+ +---v----+ +---v----+ +---v----+ | |
49 | |primary | |secondary |primary | |secondary | |
50 | |packet | |packet + |packet | |packet + | |
51 | +--------+ +--------+ +--------+ +--------+ | |
52 | | | | | | |
53 | +---v----+ +---v----+ +---v----+ +---v----+ | |
54 | |primary | |secondary |primary | |secondary | |
55 | |packet | |packet + |packet | |packet + | |
56 | +--------+ +--------+ +--------+ +--------+ | |
57 | */ | |
7dce4e6f ZC |
58 | typedef struct CompareState { |
59 | Object parent; | |
60 | ||
61 | char *pri_indev; | |
62 | char *sec_indev; | |
63 | char *outdev; | |
64 | CharDriverState *chr_pri_in; | |
65 | CharDriverState *chr_sec_in; | |
66 | CharDriverState *chr_out; | |
67 | SocketReadState pri_rs; | |
68 | SocketReadState sec_rs; | |
59509ec1 ZC |
69 | |
70 | /* hashtable to save connection */ | |
71 | GHashTable *connection_track_table; | |
7dce4e6f ZC |
72 | } CompareState; |
73 | ||
74 | typedef struct CompareClass { | |
75 | ObjectClass parent_class; | |
76 | } CompareClass; | |
77 | ||
78 | typedef struct CompareChardevProps { | |
79 | bool is_socket; | |
80 | } CompareChardevProps; | |
81 | ||
59509ec1 ZC |
82 | enum { |
83 | PRIMARY_IN = 0, | |
84 | SECONDARY_IN, | |
85 | }; | |
86 | ||
87 | static int compare_chr_send(CharDriverState *out, | |
88 | const uint8_t *buf, | |
89 | uint32_t size); | |
90 | ||
91 | /* | |
92 | * Return 0 on success, if return -1 means the pkt | |
93 | * is unsupported(arp and ipv6) and will be sent later | |
94 | */ | |
95 | static int packet_enqueue(CompareState *s, int mode) | |
96 | { | |
97 | Packet *pkt = NULL; | |
98 | ||
99 | if (mode == PRIMARY_IN) { | |
100 | pkt = packet_new(s->pri_rs.buf, s->pri_rs.packet_len); | |
101 | } else { | |
102 | pkt = packet_new(s->sec_rs.buf, s->sec_rs.packet_len); | |
103 | } | |
104 | ||
105 | if (parse_packet_early(pkt)) { | |
106 | packet_destroy(pkt, NULL); | |
107 | pkt = NULL; | |
108 | return -1; | |
109 | } | |
110 | /* TODO: get connection key from pkt */ | |
111 | ||
112 | /* | |
113 | * TODO: use connection key get conn from | |
114 | * connection_track_table | |
115 | */ | |
116 | ||
117 | /* | |
118 | * TODO: insert pkt to it's conn->primary_list | |
119 | * or conn->secondary_list | |
120 | */ | |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
125 | static int compare_chr_send(CharDriverState *out, | |
126 | const uint8_t *buf, | |
127 | uint32_t size) | |
128 | { | |
129 | int ret = 0; | |
130 | uint32_t len = htonl(size); | |
131 | ||
132 | if (!size) { | |
133 | return 0; | |
134 | } | |
135 | ||
136 | ret = qemu_chr_fe_write_all(out, (uint8_t *)&len, sizeof(len)); | |
137 | if (ret != sizeof(len)) { | |
138 | goto err; | |
139 | } | |
140 | ||
141 | ret = qemu_chr_fe_write_all(out, (uint8_t *)buf, size); | |
142 | if (ret != size) { | |
143 | goto err; | |
144 | } | |
145 | ||
146 | return 0; | |
147 | ||
148 | err: | |
149 | return ret < 0 ? ret : -EIO; | |
150 | } | |
151 | ||
7dce4e6f ZC |
152 | static char *compare_get_pri_indev(Object *obj, Error **errp) |
153 | { | |
154 | CompareState *s = COLO_COMPARE(obj); | |
155 | ||
156 | return g_strdup(s->pri_indev); | |
157 | } | |
158 | ||
159 | static void compare_set_pri_indev(Object *obj, const char *value, Error **errp) | |
160 | { | |
161 | CompareState *s = COLO_COMPARE(obj); | |
162 | ||
163 | g_free(s->pri_indev); | |
164 | s->pri_indev = g_strdup(value); | |
165 | } | |
166 | ||
167 | static char *compare_get_sec_indev(Object *obj, Error **errp) | |
168 | { | |
169 | CompareState *s = COLO_COMPARE(obj); | |
170 | ||
171 | return g_strdup(s->sec_indev); | |
172 | } | |
173 | ||
174 | static void compare_set_sec_indev(Object *obj, const char *value, Error **errp) | |
175 | { | |
176 | CompareState *s = COLO_COMPARE(obj); | |
177 | ||
178 | g_free(s->sec_indev); | |
179 | s->sec_indev = g_strdup(value); | |
180 | } | |
181 | ||
182 | static char *compare_get_outdev(Object *obj, Error **errp) | |
183 | { | |
184 | CompareState *s = COLO_COMPARE(obj); | |
185 | ||
186 | return g_strdup(s->outdev); | |
187 | } | |
188 | ||
189 | static void compare_set_outdev(Object *obj, const char *value, Error **errp) | |
190 | { | |
191 | CompareState *s = COLO_COMPARE(obj); | |
192 | ||
193 | g_free(s->outdev); | |
194 | s->outdev = g_strdup(value); | |
195 | } | |
196 | ||
197 | static void compare_pri_rs_finalize(SocketReadState *pri_rs) | |
198 | { | |
59509ec1 ZC |
199 | CompareState *s = container_of(pri_rs, CompareState, pri_rs); |
200 | ||
201 | if (packet_enqueue(s, PRIMARY_IN)) { | |
202 | trace_colo_compare_main("primary: unsupported packet in"); | |
203 | compare_chr_send(s->chr_out, pri_rs->buf, pri_rs->packet_len); | |
204 | } | |
7dce4e6f ZC |
205 | } |
206 | ||
207 | static void compare_sec_rs_finalize(SocketReadState *sec_rs) | |
208 | { | |
59509ec1 ZC |
209 | CompareState *s = container_of(sec_rs, CompareState, sec_rs); |
210 | ||
211 | if (packet_enqueue(s, SECONDARY_IN)) { | |
212 | trace_colo_compare_main("secondary: unsupported packet in"); | |
213 | } | |
7dce4e6f ZC |
214 | } |
215 | ||
216 | static int compare_chardev_opts(void *opaque, | |
217 | const char *name, const char *value, | |
218 | Error **errp) | |
219 | { | |
220 | CompareChardevProps *props = opaque; | |
221 | ||
222 | if (strcmp(name, "backend") == 0 && | |
223 | strcmp(value, "socket") == 0) { | |
224 | props->is_socket = true; | |
225 | return 0; | |
226 | } else if (strcmp(name, "host") == 0 || | |
227 | (strcmp(name, "port") == 0) || | |
228 | (strcmp(name, "server") == 0) || | |
229 | (strcmp(name, "wait") == 0) || | |
230 | (strcmp(name, "path") == 0)) { | |
231 | return 0; | |
232 | } else { | |
233 | error_setg(errp, | |
234 | "COLO-compare does not support a chardev with option %s=%s", | |
235 | name, value); | |
236 | return -1; | |
237 | } | |
238 | } | |
239 | ||
240 | /* | |
241 | * Return 0 is success. | |
242 | * Return 1 is failed. | |
243 | */ | |
244 | static int find_and_check_chardev(CharDriverState **chr, | |
245 | char *chr_name, | |
246 | Error **errp) | |
247 | { | |
248 | CompareChardevProps props; | |
249 | ||
250 | *chr = qemu_chr_find(chr_name); | |
251 | if (*chr == NULL) { | |
252 | error_setg(errp, "Device '%s' not found", | |
253 | chr_name); | |
254 | return 1; | |
255 | } | |
256 | ||
257 | memset(&props, 0, sizeof(props)); | |
258 | if (qemu_opt_foreach((*chr)->opts, compare_chardev_opts, &props, errp)) { | |
259 | return 1; | |
260 | } | |
261 | ||
262 | if (!props.is_socket) { | |
263 | error_setg(errp, "chardev \"%s\" is not a tcp socket", | |
264 | chr_name); | |
265 | return 1; | |
266 | } | |
267 | return 0; | |
268 | } | |
269 | ||
270 | /* | |
271 | * Called from the main thread on the primary | |
272 | * to setup colo-compare. | |
273 | */ | |
274 | static void colo_compare_complete(UserCreatable *uc, Error **errp) | |
275 | { | |
276 | CompareState *s = COLO_COMPARE(uc); | |
277 | ||
278 | if (!s->pri_indev || !s->sec_indev || !s->outdev) { | |
279 | error_setg(errp, "colo compare needs 'primary_in' ," | |
280 | "'secondary_in','outdev' property set"); | |
281 | return; | |
282 | } else if (!strcmp(s->pri_indev, s->outdev) || | |
283 | !strcmp(s->sec_indev, s->outdev) || | |
284 | !strcmp(s->pri_indev, s->sec_indev)) { | |
285 | error_setg(errp, "'indev' and 'outdev' could not be same " | |
286 | "for compare module"); | |
287 | return; | |
288 | } | |
289 | ||
290 | if (find_and_check_chardev(&s->chr_pri_in, s->pri_indev, errp)) { | |
291 | return; | |
292 | } | |
293 | ||
294 | if (find_and_check_chardev(&s->chr_sec_in, s->sec_indev, errp)) { | |
295 | return; | |
296 | } | |
297 | ||
298 | if (find_and_check_chardev(&s->chr_out, s->outdev, errp)) { | |
299 | return; | |
300 | } | |
301 | ||
302 | qemu_chr_fe_claim_no_fail(s->chr_pri_in); | |
303 | ||
304 | qemu_chr_fe_claim_no_fail(s->chr_sec_in); | |
305 | ||
306 | qemu_chr_fe_claim_no_fail(s->chr_out); | |
307 | ||
308 | net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize); | |
309 | net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize); | |
310 | ||
59509ec1 ZC |
311 | /* use g_hash_table_new_full() to new a hashtable */ |
312 | ||
7dce4e6f ZC |
313 | return; |
314 | } | |
315 | ||
316 | static void colo_compare_class_init(ObjectClass *oc, void *data) | |
317 | { | |
318 | UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); | |
319 | ||
320 | ucc->complete = colo_compare_complete; | |
321 | } | |
322 | ||
323 | static void colo_compare_init(Object *obj) | |
324 | { | |
325 | object_property_add_str(obj, "primary_in", | |
326 | compare_get_pri_indev, compare_set_pri_indev, | |
327 | NULL); | |
328 | object_property_add_str(obj, "secondary_in", | |
329 | compare_get_sec_indev, compare_set_sec_indev, | |
330 | NULL); | |
331 | object_property_add_str(obj, "outdev", | |
332 | compare_get_outdev, compare_set_outdev, | |
333 | NULL); | |
334 | } | |
335 | ||
336 | static void colo_compare_finalize(Object *obj) | |
337 | { | |
338 | CompareState *s = COLO_COMPARE(obj); | |
339 | ||
340 | if (s->chr_pri_in) { | |
341 | qemu_chr_add_handlers(s->chr_pri_in, NULL, NULL, NULL, NULL); | |
342 | qemu_chr_fe_release(s->chr_pri_in); | |
343 | } | |
344 | if (s->chr_sec_in) { | |
345 | qemu_chr_add_handlers(s->chr_sec_in, NULL, NULL, NULL, NULL); | |
346 | qemu_chr_fe_release(s->chr_sec_in); | |
347 | } | |
348 | if (s->chr_out) { | |
349 | qemu_chr_fe_release(s->chr_out); | |
350 | } | |
351 | ||
352 | g_free(s->pri_indev); | |
353 | g_free(s->sec_indev); | |
354 | g_free(s->outdev); | |
355 | } | |
356 | ||
357 | static const TypeInfo colo_compare_info = { | |
358 | .name = TYPE_COLO_COMPARE, | |
359 | .parent = TYPE_OBJECT, | |
360 | .instance_size = sizeof(CompareState), | |
361 | .instance_init = colo_compare_init, | |
362 | .instance_finalize = colo_compare_finalize, | |
363 | .class_size = sizeof(CompareClass), | |
364 | .class_init = colo_compare_class_init, | |
365 | .interfaces = (InterfaceInfo[]) { | |
366 | { TYPE_USER_CREATABLE }, | |
367 | { } | |
368 | } | |
369 | }; | |
370 | ||
371 | static void register_types(void) | |
372 | { | |
373 | type_register_static(&colo_compare_info); | |
374 | } | |
375 | ||
376 | type_init(register_types); |