2 * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO)
3 * (a.k.a. Fault Tolerance or Continuous Replication)
5 * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
6 * Copyright (c) 2016 FUJITSU LIMITED
7 * Copyright (c) 2016 Intel Corporation
9 * Author: Zhang Chen <zhangchen.fnst@cn.fujitsu.com>
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.
15 #include "qemu/osdep.h"
16 #include "qemu/error-report.h"
18 #include "qemu-common.h"
19 #include "qapi/qmp/qerror.h"
20 #include "qapi/error.h"
23 #include "qom/object_interfaces.h"
25 #include "qom/object.h"
26 #include "qemu/typedefs.h"
27 #include "net/queue.h"
28 #include "sysemu/char.h"
29 #include "qemu/sockets.h"
30 #include "qapi-visit.h"
33 #define TYPE_COLO_COMPARE "colo-compare"
34 #define COLO_COMPARE(obj) \
35 OBJECT_CHECK(CompareState, (obj), TYPE_COLO_COMPARE)
37 #define COMPARE_READ_LEN_MAX NET_BUFSIZE
38 #define MAX_QUEUE_SIZE 1024
40 /* TODO: Should be configurable */
41 #define REGULAR_PACKET_CHECK_MS 3000
46 +---------------+ +---------------+ +---------------+
47 |conn list +--->conn +--------->conn |
48 +---------------+ +---------------+ +---------------+
50 +---------------+ +---v----+ +---v----+ +---v----+ +---v----+
51 |primary | |secondary |primary | |secondary
52 |packet | |packet + |packet | |packet +
53 +--------+ +--------+ +--------+ +--------+
55 +---v----+ +---v----+ +---v----+ +---v----+
56 |primary | |secondary |primary | |secondary
57 |packet | |packet + |packet | |packet +
58 +--------+ +--------+ +--------+ +--------+
60 +---v----+ +---v----+ +---v----+ +---v----+
61 |primary | |secondary |primary | |secondary
62 |packet | |packet + |packet | |packet +
63 +--------+ +--------+ +--------+ +--------+
65 typedef struct CompareState
{
71 CharBackend chr_pri_in
;
72 CharBackend chr_sec_in
;
74 SocketReadState pri_rs
;
75 SocketReadState sec_rs
;
77 /* connection list: the connections belonged to this NIC could be found
79 * element type: Connection
82 /* hashtable to save connection */
83 GHashTable
*connection_track_table
;
84 /* compare thread, a thread for each NIC */
87 GMainContext
*worker_context
;
88 GMainLoop
*compare_loop
;
91 typedef struct CompareClass
{
92 ObjectClass parent_class
;
100 static int compare_chr_send(CharBackend
*out
,
104 static gint
seq_sorter(Packet
*a
, Packet
*b
, gpointer data
)
106 struct tcphdr
*atcp
, *btcp
;
108 atcp
= (struct tcphdr
*)(a
->transport_header
);
109 btcp
= (struct tcphdr
*)(b
->transport_header
);
110 return ntohl(atcp
->th_seq
) - ntohl(btcp
->th_seq
);
114 * Return 0 on success, if return -1 means the pkt
115 * is unsupported(arp and ipv6) and will be sent later
117 static int packet_enqueue(CompareState
*s
, int mode
)
123 if (mode
== PRIMARY_IN
) {
124 pkt
= packet_new(s
->pri_rs
.buf
, s
->pri_rs
.packet_len
);
126 pkt
= packet_new(s
->sec_rs
.buf
, s
->sec_rs
.packet_len
);
129 if (parse_packet_early(pkt
)) {
130 packet_destroy(pkt
, NULL
);
134 fill_connection_key(pkt
, &key
);
136 conn
= connection_get(s
->connection_track_table
,
140 if (!conn
->processing
) {
141 g_queue_push_tail(&s
->conn_list
, conn
);
142 conn
->processing
= true;
145 if (mode
== PRIMARY_IN
) {
146 if (g_queue_get_length(&conn
->primary_list
) <=
148 g_queue_push_tail(&conn
->primary_list
, pkt
);
149 if (conn
->ip_proto
== IPPROTO_TCP
) {
150 g_queue_sort(&conn
->primary_list
,
151 (GCompareDataFunc
)seq_sorter
,
155 error_report("colo compare primary queue size too big,"
159 if (g_queue_get_length(&conn
->secondary_list
) <=
161 g_queue_push_tail(&conn
->secondary_list
, pkt
);
162 if (conn
->ip_proto
== IPPROTO_TCP
) {
163 g_queue_sort(&conn
->secondary_list
,
164 (GCompareDataFunc
)seq_sorter
,
168 error_report("colo compare secondary queue size too big,"
177 * The IP packets sent by primary and secondary
178 * will be compared in here
179 * TODO support ip fragment, Out-Of-Order
180 * return: 0 means packet same
181 * > 0 || < 0 means packet different
183 static int colo_packet_compare_common(Packet
*ppkt
, Packet
*spkt
, int offset
)
185 if (trace_event_get_state(TRACE_COLO_COMPARE_MISCOMPARE
)) {
186 char pri_ip_src
[20], pri_ip_dst
[20], sec_ip_src
[20], sec_ip_dst
[20];
188 strcpy(pri_ip_src
, inet_ntoa(ppkt
->ip
->ip_src
));
189 strcpy(pri_ip_dst
, inet_ntoa(ppkt
->ip
->ip_dst
));
190 strcpy(sec_ip_src
, inet_ntoa(spkt
->ip
->ip_src
));
191 strcpy(sec_ip_dst
, inet_ntoa(spkt
->ip
->ip_dst
));
193 trace_colo_compare_ip_info(ppkt
->size
, pri_ip_src
,
194 pri_ip_dst
, spkt
->size
,
195 sec_ip_src
, sec_ip_dst
);
198 if (ppkt
->size
== spkt
->size
) {
199 return memcmp(ppkt
->data
+ offset
, spkt
->data
+ offset
,
200 spkt
->size
- offset
);
202 trace_colo_compare_main("Net packet size are not the same");
208 * Called from the compare thread on the primary
209 * for compare tcp packet
210 * compare_tcp copied from Dr. David Alan Gilbert's branch
212 static int colo_packet_compare_tcp(Packet
*spkt
, Packet
*ppkt
)
214 struct tcphdr
*ptcp
, *stcp
;
217 trace_colo_compare_main("compare tcp");
219 ptcp
= (struct tcphdr
*)ppkt
->transport_header
;
220 stcp
= (struct tcphdr
*)spkt
->transport_header
;
223 * The 'identification' field in the IP header is *very* random
224 * it almost never matches. Fudge this by ignoring differences in
225 * unfragmented packets; they'll normally sort themselves out if different
226 * anyway, and it should recover at the TCP level.
227 * An alternative would be to get both the primary and secondary to rewrite
228 * somehow; but that would need some sync traffic to sync the state
230 if (ntohs(ppkt
->ip
->ip_off
) & IP_DF
) {
231 spkt
->ip
->ip_id
= ppkt
->ip
->ip_id
;
232 /* and the sum will be different if the IDs were different */
233 spkt
->ip
->ip_sum
= ppkt
->ip
->ip_sum
;
237 * Check tcp header length for tcp option field.
238 * th_off > 5 means this tcp packet have options field.
239 * The tcp options maybe always different.
242 * TCP Timestamps option (TSopt):
247 * +-------+-------+---------------------+---------------------+
248 * |Kind=8 | 10 | TS Value (TSval) |TS Echo Reply (TSecr)|
249 * +-------+-------+---------------------+---------------------+
252 * In this case the primary guest's timestamp always different with
253 * the secondary guest's timestamp. COLO just focus on payload,
254 * so we just need skip this field.
256 if (ptcp
->th_off
> 5) {
257 ptrdiff_t tcp_offset
;
258 tcp_offset
= ppkt
->transport_header
- (uint8_t *)ppkt
->data
259 + (ptcp
->th_off
* 4);
260 res
= colo_packet_compare_common(ppkt
, spkt
, tcp_offset
);
261 } else if (ptcp
->th_sum
== stcp
->th_sum
) {
262 res
= colo_packet_compare_common(ppkt
, spkt
, ETH_HLEN
);
267 if (res
!= 0 && trace_event_get_state(TRACE_COLO_COMPARE_MISCOMPARE
)) {
268 char pri_ip_src
[20], pri_ip_dst
[20], sec_ip_src
[20], sec_ip_dst
[20];
270 strcpy(pri_ip_src
, inet_ntoa(ppkt
->ip
->ip_src
));
271 strcpy(pri_ip_dst
, inet_ntoa(ppkt
->ip
->ip_dst
));
272 strcpy(sec_ip_src
, inet_ntoa(spkt
->ip
->ip_src
));
273 strcpy(sec_ip_dst
, inet_ntoa(spkt
->ip
->ip_dst
));
275 trace_colo_compare_ip_info(ppkt
->size
, pri_ip_src
,
276 pri_ip_dst
, spkt
->size
,
277 sec_ip_src
, sec_ip_dst
);
279 trace_colo_compare_tcp_info("pri tcp packet",
285 trace_colo_compare_tcp_info("sec tcp packet",
291 qemu_hexdump((char *)ppkt
->data
, stderr
,
292 "colo-compare ppkt", ppkt
->size
);
293 qemu_hexdump((char *)spkt
->data
, stderr
,
294 "colo-compare spkt", spkt
->size
);
301 * Called from the compare thread on the primary
302 * for compare udp packet
304 static int colo_packet_compare_udp(Packet
*spkt
, Packet
*ppkt
)
307 int network_header_length
= ppkt
->ip
->ip_hl
* 4;
309 trace_colo_compare_main("compare udp");
312 * Because of ppkt and spkt are both in the same connection,
313 * The ppkt's src ip, dst ip, src port, dst port, ip_proto all are
314 * same with spkt. In addition, IP header's Identification is a random
315 * field, we can handle it in IP fragmentation function later.
316 * COLO just concern the response net packet payload from primary guest
317 * and secondary guest are same or not, So we ignored all IP header include
318 * other field like TOS,TTL,IP Checksum. we only need to compare
319 * the ip payload here.
321 ret
= colo_packet_compare_common(ppkt
, spkt
,
322 network_header_length
+ ETH_HLEN
);
325 trace_colo_compare_udp_miscompare("primary pkt size", ppkt
->size
);
326 trace_colo_compare_udp_miscompare("Secondary pkt size", spkt
->size
);
327 if (trace_event_get_state(TRACE_COLO_COMPARE_MISCOMPARE
)) {
328 qemu_hexdump((char *)ppkt
->data
, stderr
, "colo-compare pri pkt",
330 qemu_hexdump((char *)spkt
->data
, stderr
, "colo-compare sec pkt",
339 * Called from the compare thread on the primary
340 * for compare icmp packet
342 static int colo_packet_compare_icmp(Packet
*spkt
, Packet
*ppkt
)
344 int network_header_length
= ppkt
->ip
->ip_hl
* 4;
346 trace_colo_compare_main("compare icmp");
349 * Because of ppkt and spkt are both in the same connection,
350 * The ppkt's src ip, dst ip, src port, dst port, ip_proto all are
351 * same with spkt. In addition, IP header's Identification is a random
352 * field, we can handle it in IP fragmentation function later.
353 * COLO just concern the response net packet payload from primary guest
354 * and secondary guest are same or not, So we ignored all IP header include
355 * other field like TOS,TTL,IP Checksum. we only need to compare
356 * the ip payload here.
358 if (colo_packet_compare_common(ppkt
, spkt
,
359 network_header_length
+ ETH_HLEN
)) {
360 trace_colo_compare_icmp_miscompare("primary pkt size",
362 trace_colo_compare_icmp_miscompare("Secondary pkt size",
364 if (trace_event_get_state(TRACE_COLO_COMPARE_MISCOMPARE
)) {
365 qemu_hexdump((char *)ppkt
->data
, stderr
, "colo-compare pri pkt",
367 qemu_hexdump((char *)spkt
->data
, stderr
, "colo-compare sec pkt",
377 * Called from the compare thread on the primary
378 * for compare other packet
380 static int colo_packet_compare_other(Packet
*spkt
, Packet
*ppkt
)
382 trace_colo_compare_main("compare other");
383 if (trace_event_get_state(TRACE_COLO_COMPARE_MISCOMPARE
)) {
384 char pri_ip_src
[20], pri_ip_dst
[20], sec_ip_src
[20], sec_ip_dst
[20];
386 strcpy(pri_ip_src
, inet_ntoa(ppkt
->ip
->ip_src
));
387 strcpy(pri_ip_dst
, inet_ntoa(ppkt
->ip
->ip_dst
));
388 strcpy(sec_ip_src
, inet_ntoa(spkt
->ip
->ip_src
));
389 strcpy(sec_ip_dst
, inet_ntoa(spkt
->ip
->ip_dst
));
391 trace_colo_compare_ip_info(ppkt
->size
, pri_ip_src
,
392 pri_ip_dst
, spkt
->size
,
393 sec_ip_src
, sec_ip_dst
);
396 return colo_packet_compare_common(ppkt
, spkt
, 0);
399 static int colo_old_packet_check_one(Packet
*pkt
, int64_t *check_time
)
401 int64_t now
= qemu_clock_get_ms(QEMU_CLOCK_HOST
);
403 if ((now
- pkt
->creation_ms
) > (*check_time
)) {
404 trace_colo_old_packet_check_found(pkt
->creation_ms
);
411 static int colo_old_packet_check_one_conn(Connection
*conn
,
414 GList
*result
= NULL
;
415 int64_t check_time
= REGULAR_PACKET_CHECK_MS
;
417 result
= g_queue_find_custom(&conn
->primary_list
,
419 (GCompareFunc
)colo_old_packet_check_one
);
422 /* do checkpoint will flush old packet */
423 /* TODO: colo_notify_checkpoint();*/
431 * Look for old packets that the secondary hasn't matched,
432 * if we have some then we have to checkpoint to wake
435 static void colo_old_packet_check(void *opaque
)
437 CompareState
*s
= opaque
;
440 * If we find one old packet, stop finding job and notify
441 * COLO frame do checkpoint.
443 g_queue_find_custom(&s
->conn_list
, NULL
,
444 (GCompareFunc
)colo_old_packet_check_one_conn
);
448 * Called from the compare thread on the primary
449 * for compare connection
451 static void colo_compare_connection(void *opaque
, void *user_data
)
453 CompareState
*s
= user_data
;
454 Connection
*conn
= opaque
;
456 GList
*result
= NULL
;
459 while (!g_queue_is_empty(&conn
->primary_list
) &&
460 !g_queue_is_empty(&conn
->secondary_list
)) {
461 pkt
= g_queue_pop_tail(&conn
->primary_list
);
462 switch (conn
->ip_proto
) {
464 result
= g_queue_find_custom(&conn
->secondary_list
,
465 pkt
, (GCompareFunc
)colo_packet_compare_tcp
);
468 result
= g_queue_find_custom(&conn
->secondary_list
,
469 pkt
, (GCompareFunc
)colo_packet_compare_udp
);
472 result
= g_queue_find_custom(&conn
->secondary_list
,
473 pkt
, (GCompareFunc
)colo_packet_compare_icmp
);
476 result
= g_queue_find_custom(&conn
->secondary_list
,
477 pkt
, (GCompareFunc
)colo_packet_compare_other
);
482 ret
= compare_chr_send(&s
->chr_out
, pkt
->data
, pkt
->size
);
484 error_report("colo_send_primary_packet failed");
486 trace_colo_compare_main("packet same and release packet");
487 g_queue_remove(&conn
->secondary_list
, result
->data
);
488 packet_destroy(pkt
, NULL
);
491 * If one packet arrive late, the secondary_list or
492 * primary_list will be empty, so we can't compare it
493 * until next comparison.
495 trace_colo_compare_main("packet different");
496 g_queue_push_tail(&conn
->primary_list
, pkt
);
497 /* TODO: colo_notify_checkpoint();*/
503 static int compare_chr_send(CharBackend
*out
,
508 uint32_t len
= htonl(size
);
514 ret
= qemu_chr_fe_write_all(out
, (uint8_t *)&len
, sizeof(len
));
515 if (ret
!= sizeof(len
)) {
519 ret
= qemu_chr_fe_write_all(out
, (uint8_t *)buf
, size
);
527 return ret
< 0 ? ret
: -EIO
;
530 static int compare_chr_can_read(void *opaque
)
532 return COMPARE_READ_LEN_MAX
;
536 * Called from the main thread on the primary for packets
537 * arriving over the socket from the primary.
539 static void compare_pri_chr_in(void *opaque
, const uint8_t *buf
, int size
)
541 CompareState
*s
= COLO_COMPARE(opaque
);
544 ret
= net_fill_rstate(&s
->pri_rs
, buf
, size
);
546 qemu_chr_fe_set_handlers(&s
->chr_pri_in
, NULL
, NULL
, NULL
,
548 error_report("colo-compare primary_in error");
553 * Called from the main thread on the primary for packets
554 * arriving over the socket from the secondary.
556 static void compare_sec_chr_in(void *opaque
, const uint8_t *buf
, int size
)
558 CompareState
*s
= COLO_COMPARE(opaque
);
561 ret
= net_fill_rstate(&s
->sec_rs
, buf
, size
);
563 qemu_chr_fe_set_handlers(&s
->chr_sec_in
, NULL
, NULL
, NULL
,
565 error_report("colo-compare secondary_in error");
570 * Check old packet regularly so it can watch for any packets
571 * that the secondary hasn't produced equivalents of.
573 static gboolean
check_old_packet_regular(void *opaque
)
575 CompareState
*s
= opaque
;
577 /* if have old packet we will notify checkpoint */
578 colo_old_packet_check(s
);
583 static void *colo_compare_thread(void *opaque
)
585 CompareState
*s
= opaque
;
586 GSource
*timeout_source
;
588 s
->worker_context
= g_main_context_new();
590 qemu_chr_fe_set_handlers(&s
->chr_pri_in
, compare_chr_can_read
,
591 compare_pri_chr_in
, NULL
, s
, s
->worker_context
, true);
592 qemu_chr_fe_set_handlers(&s
->chr_sec_in
, compare_chr_can_read
,
593 compare_sec_chr_in
, NULL
, s
, s
->worker_context
, true);
595 s
->compare_loop
= g_main_loop_new(s
->worker_context
, FALSE
);
597 /* To kick any packets that the secondary doesn't match */
598 timeout_source
= g_timeout_source_new(REGULAR_PACKET_CHECK_MS
);
599 g_source_set_callback(timeout_source
,
600 (GSourceFunc
)check_old_packet_regular
, s
, NULL
);
601 g_source_attach(timeout_source
, s
->worker_context
);
603 g_main_loop_run(s
->compare_loop
);
605 g_source_unref(timeout_source
);
606 g_main_loop_unref(s
->compare_loop
);
607 g_main_context_unref(s
->worker_context
);
611 static char *compare_get_pri_indev(Object
*obj
, Error
**errp
)
613 CompareState
*s
= COLO_COMPARE(obj
);
615 return g_strdup(s
->pri_indev
);
618 static void compare_set_pri_indev(Object
*obj
, const char *value
, Error
**errp
)
620 CompareState
*s
= COLO_COMPARE(obj
);
622 g_free(s
->pri_indev
);
623 s
->pri_indev
= g_strdup(value
);
626 static char *compare_get_sec_indev(Object
*obj
, Error
**errp
)
628 CompareState
*s
= COLO_COMPARE(obj
);
630 return g_strdup(s
->sec_indev
);
633 static void compare_set_sec_indev(Object
*obj
, const char *value
, Error
**errp
)
635 CompareState
*s
= COLO_COMPARE(obj
);
637 g_free(s
->sec_indev
);
638 s
->sec_indev
= g_strdup(value
);
641 static char *compare_get_outdev(Object
*obj
, Error
**errp
)
643 CompareState
*s
= COLO_COMPARE(obj
);
645 return g_strdup(s
->outdev
);
648 static void compare_set_outdev(Object
*obj
, const char *value
, Error
**errp
)
650 CompareState
*s
= COLO_COMPARE(obj
);
653 s
->outdev
= g_strdup(value
);
656 static void compare_pri_rs_finalize(SocketReadState
*pri_rs
)
658 CompareState
*s
= container_of(pri_rs
, CompareState
, pri_rs
);
660 if (packet_enqueue(s
, PRIMARY_IN
)) {
661 trace_colo_compare_main("primary: unsupported packet in");
662 compare_chr_send(&s
->chr_out
, pri_rs
->buf
, pri_rs
->packet_len
);
664 /* compare connection */
665 g_queue_foreach(&s
->conn_list
, colo_compare_connection
, s
);
669 static void compare_sec_rs_finalize(SocketReadState
*sec_rs
)
671 CompareState
*s
= container_of(sec_rs
, CompareState
, sec_rs
);
673 if (packet_enqueue(s
, SECONDARY_IN
)) {
674 trace_colo_compare_main("secondary: unsupported packet in");
676 /* compare connection */
677 g_queue_foreach(&s
->conn_list
, colo_compare_connection
, s
);
683 * Return 0 is success.
684 * Return 1 is failed.
686 static int find_and_check_chardev(Chardev
**chr
,
690 *chr
= qemu_chr_find(chr_name
);
692 error_setg(errp
, "Device '%s' not found",
697 if (!qemu_chr_has_feature(*chr
, QEMU_CHAR_FEATURE_RECONNECTABLE
)) {
698 error_setg(errp
, "chardev \"%s\" is not reconnectable",
707 * Called from the main thread on the primary
708 * to setup colo-compare.
710 static void colo_compare_complete(UserCreatable
*uc
, Error
**errp
)
712 CompareState
*s
= COLO_COMPARE(uc
);
714 char thread_name
[64];
715 static int compare_id
;
717 if (!s
->pri_indev
|| !s
->sec_indev
|| !s
->outdev
) {
718 error_setg(errp
, "colo compare needs 'primary_in' ,"
719 "'secondary_in','outdev' property set");
721 } else if (!strcmp(s
->pri_indev
, s
->outdev
) ||
722 !strcmp(s
->sec_indev
, s
->outdev
) ||
723 !strcmp(s
->pri_indev
, s
->sec_indev
)) {
724 error_setg(errp
, "'indev' and 'outdev' could not be same "
725 "for compare module");
729 if (find_and_check_chardev(&chr
, s
->pri_indev
, errp
) ||
730 !qemu_chr_fe_init(&s
->chr_pri_in
, chr
, errp
)) {
734 if (find_and_check_chardev(&chr
, s
->sec_indev
, errp
) ||
735 !qemu_chr_fe_init(&s
->chr_sec_in
, chr
, errp
)) {
739 if (find_and_check_chardev(&chr
, s
->outdev
, errp
) ||
740 !qemu_chr_fe_init(&s
->chr_out
, chr
, errp
)) {
744 net_socket_rs_init(&s
->pri_rs
, compare_pri_rs_finalize
);
745 net_socket_rs_init(&s
->sec_rs
, compare_sec_rs_finalize
);
747 g_queue_init(&s
->conn_list
);
749 s
->connection_track_table
= g_hash_table_new_full(connection_key_hash
,
750 connection_key_equal
,
754 sprintf(thread_name
, "colo-compare %d", compare_id
);
755 qemu_thread_create(&s
->thread
, thread_name
,
756 colo_compare_thread
, s
,
757 QEMU_THREAD_JOINABLE
);
763 static void colo_flush_packets(void *opaque
, void *user_data
)
765 CompareState
*s
= user_data
;
766 Connection
*conn
= opaque
;
769 while (!g_queue_is_empty(&conn
->primary_list
)) {
770 pkt
= g_queue_pop_head(&conn
->primary_list
);
771 compare_chr_send(&s
->chr_out
, pkt
->data
, pkt
->size
);
772 packet_destroy(pkt
, NULL
);
774 while (!g_queue_is_empty(&conn
->secondary_list
)) {
775 pkt
= g_queue_pop_head(&conn
->secondary_list
);
776 packet_destroy(pkt
, NULL
);
780 static void colo_compare_class_init(ObjectClass
*oc
, void *data
)
782 UserCreatableClass
*ucc
= USER_CREATABLE_CLASS(oc
);
784 ucc
->complete
= colo_compare_complete
;
787 static void colo_compare_init(Object
*obj
)
789 object_property_add_str(obj
, "primary_in",
790 compare_get_pri_indev
, compare_set_pri_indev
,
792 object_property_add_str(obj
, "secondary_in",
793 compare_get_sec_indev
, compare_set_sec_indev
,
795 object_property_add_str(obj
, "outdev",
796 compare_get_outdev
, compare_set_outdev
,
800 static void colo_compare_finalize(Object
*obj
)
802 CompareState
*s
= COLO_COMPARE(obj
);
804 qemu_chr_fe_set_handlers(&s
->chr_pri_in
, NULL
, NULL
, NULL
, NULL
,
805 s
->worker_context
, true);
806 qemu_chr_fe_set_handlers(&s
->chr_sec_in
, NULL
, NULL
, NULL
, NULL
,
807 s
->worker_context
, true);
808 qemu_chr_fe_deinit(&s
->chr_out
);
810 g_main_loop_quit(s
->compare_loop
);
811 qemu_thread_join(&s
->thread
);
813 /* Release all unhandled packets after compare thead exited */
814 g_queue_foreach(&s
->conn_list
, colo_flush_packets
, s
);
816 g_queue_clear(&s
->conn_list
);
818 g_hash_table_destroy(s
->connection_track_table
);
819 g_free(s
->pri_indev
);
820 g_free(s
->sec_indev
);
824 static const TypeInfo colo_compare_info
= {
825 .name
= TYPE_COLO_COMPARE
,
826 .parent
= TYPE_OBJECT
,
827 .instance_size
= sizeof(CompareState
),
828 .instance_init
= colo_compare_init
,
829 .instance_finalize
= colo_compare_finalize
,
830 .class_size
= sizeof(CompareClass
),
831 .class_init
= colo_compare_class_init
,
832 .interfaces
= (InterfaceInfo
[]) {
833 { TYPE_USER_CREATABLE
},
838 static void register_types(void)
840 type_register_static(&colo_compare_info
);
843 type_init(register_types
);