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 "chardev/char-fe.h"
29 #include "qemu/sockets.h"
30 #include "qapi-visit.h"
32 #include "sysemu/iothread.h"
34 #define TYPE_COLO_COMPARE "colo-compare"
35 #define COLO_COMPARE(obj) \
36 OBJECT_CHECK(CompareState, (obj), TYPE_COLO_COMPARE)
38 #define COMPARE_READ_LEN_MAX NET_BUFSIZE
39 #define MAX_QUEUE_SIZE 1024
41 /* TODO: Should be configurable */
42 #define REGULAR_PACKET_CHECK_MS 3000
47 * +---------------+ +---------------+ +---------------+
48 * | conn list + - > conn + ------- > conn + -- > ......
49 * +---------------+ +---------------+ +---------------+
51 * +---------------+ +---v----+ +---v----+ +---v----+ +---v----+
52 * |primary | |secondary |primary | |secondary
53 * |packet | |packet + |packet | |packet +
54 * +--------+ +--------+ +--------+ +--------+
56 * +---v----+ +---v----+ +---v----+ +---v----+
57 * |primary | |secondary |primary | |secondary
58 * |packet | |packet + |packet | |packet +
59 * +--------+ +--------+ +--------+ +--------+
61 * +---v----+ +---v----+ +---v----+ +---v----+
62 * |primary | |secondary |primary | |secondary
63 * |packet | |packet + |packet | |packet +
64 * +--------+ +--------+ +--------+ +--------+
66 typedef struct CompareState
{
72 CharBackend chr_pri_in
;
73 CharBackend chr_sec_in
;
75 SocketReadState pri_rs
;
76 SocketReadState sec_rs
;
80 * Record the connection that through the NIC
81 * Element type: Connection
84 /* Record the connection without repetition */
85 GHashTable
*connection_track_table
;
88 GMainContext
*worker_context
;
89 QEMUTimer
*packet_check_timer
;
92 typedef struct CompareClass
{
93 ObjectClass parent_class
;
101 static int compare_chr_send(CompareState
*s
,
104 uint32_t vnet_hdr_len
);
106 static gint
seq_sorter(Packet
*a
, Packet
*b
, gpointer data
)
108 struct tcphdr
*atcp
, *btcp
;
110 atcp
= (struct tcphdr
*)(a
->transport_header
);
111 btcp
= (struct tcphdr
*)(b
->transport_header
);
112 return ntohl(atcp
->th_seq
) - ntohl(btcp
->th_seq
);
116 * Return 0 on success, if return -1 means the pkt
117 * is unsupported(arp and ipv6) and will be sent later
119 static int packet_enqueue(CompareState
*s
, int mode
)
125 if (mode
== PRIMARY_IN
) {
126 pkt
= packet_new(s
->pri_rs
.buf
,
127 s
->pri_rs
.packet_len
,
128 s
->pri_rs
.vnet_hdr_len
);
130 pkt
= packet_new(s
->sec_rs
.buf
,
131 s
->sec_rs
.packet_len
,
132 s
->sec_rs
.vnet_hdr_len
);
135 if (parse_packet_early(pkt
)) {
136 packet_destroy(pkt
, NULL
);
140 fill_connection_key(pkt
, &key
);
142 conn
= connection_get(s
->connection_track_table
,
146 if (!conn
->processing
) {
147 g_queue_push_tail(&s
->conn_list
, conn
);
148 conn
->processing
= true;
151 if (mode
== PRIMARY_IN
) {
152 if (g_queue_get_length(&conn
->primary_list
) <=
154 g_queue_push_tail(&conn
->primary_list
, pkt
);
155 if (conn
->ip_proto
== IPPROTO_TCP
) {
156 g_queue_sort(&conn
->primary_list
,
157 (GCompareDataFunc
)seq_sorter
,
161 error_report("colo compare primary queue size too big,"
165 if (g_queue_get_length(&conn
->secondary_list
) <=
167 g_queue_push_tail(&conn
->secondary_list
, pkt
);
168 if (conn
->ip_proto
== IPPROTO_TCP
) {
169 g_queue_sort(&conn
->secondary_list
,
170 (GCompareDataFunc
)seq_sorter
,
174 error_report("colo compare secondary queue size too big,"
183 * The IP packets sent by primary and secondary
184 * will be compared in here
185 * TODO support ip fragment, Out-Of-Order
186 * return: 0 means packet same
187 * > 0 || < 0 means packet different
189 static int colo_packet_compare_common(Packet
*ppkt
,
194 if (trace_event_get_state_backends(TRACE_COLO_COMPARE_MISCOMPARE
)) {
195 char pri_ip_src
[20], pri_ip_dst
[20], sec_ip_src
[20], sec_ip_dst
[20];
197 strcpy(pri_ip_src
, inet_ntoa(ppkt
->ip
->ip_src
));
198 strcpy(pri_ip_dst
, inet_ntoa(ppkt
->ip
->ip_dst
));
199 strcpy(sec_ip_src
, inet_ntoa(spkt
->ip
->ip_src
));
200 strcpy(sec_ip_dst
, inet_ntoa(spkt
->ip
->ip_dst
));
202 trace_colo_compare_ip_info(ppkt
->size
, pri_ip_src
,
203 pri_ip_dst
, spkt
->size
,
204 sec_ip_src
, sec_ip_dst
);
207 poffset
= ppkt
->vnet_hdr_len
+ poffset
;
208 soffset
= ppkt
->vnet_hdr_len
+ soffset
;
210 if (ppkt
->size
- poffset
== spkt
->size
- soffset
) {
211 return memcmp(ppkt
->data
+ poffset
,
212 spkt
->data
+ soffset
,
213 spkt
->size
- soffset
);
215 trace_colo_compare_main("Net packet size are not the same");
221 * Called from the compare thread on the primary
222 * for compare tcp packet
223 * compare_tcp copied from Dr. David Alan Gilbert's branch
225 static int colo_packet_compare_tcp(Packet
*spkt
, Packet
*ppkt
)
227 struct tcphdr
*ptcp
, *stcp
;
230 trace_colo_compare_main("compare tcp");
232 ptcp
= (struct tcphdr
*)ppkt
->transport_header
;
233 stcp
= (struct tcphdr
*)spkt
->transport_header
;
236 * The 'identification' field in the IP header is *very* random
237 * it almost never matches. Fudge this by ignoring differences in
238 * unfragmented packets; they'll normally sort themselves out if different
239 * anyway, and it should recover at the TCP level.
240 * An alternative would be to get both the primary and secondary to rewrite
241 * somehow; but that would need some sync traffic to sync the state
243 if (ntohs(ppkt
->ip
->ip_off
) & IP_DF
) {
244 spkt
->ip
->ip_id
= ppkt
->ip
->ip_id
;
245 /* and the sum will be different if the IDs were different */
246 spkt
->ip
->ip_sum
= ppkt
->ip
->ip_sum
;
250 * Check tcp header length for tcp option field.
251 * th_off > 5 means this tcp packet have options field.
252 * The tcp options maybe always different.
255 * TCP Timestamps option (TSopt):
260 * +-------+-------+---------------------+---------------------+
261 * |Kind=8 | 10 | TS Value (TSval) |TS Echo Reply (TSecr)|
262 * +-------+-------+---------------------+---------------------+
265 * In this case the primary guest's timestamp always different with
266 * the secondary guest's timestamp. COLO just focus on payload,
267 * so we just need skip this field.
269 if (ptcp
->th_off
> 5) {
270 ptrdiff_t ptcp_offset
, stcp_offset
;
272 ptcp_offset
= ppkt
->transport_header
- (uint8_t *)ppkt
->data
273 + (ptcp
->th_off
* 4) - ppkt
->vnet_hdr_len
;
274 stcp_offset
= spkt
->transport_header
- (uint8_t *)spkt
->data
275 + (stcp
->th_off
* 4) - spkt
->vnet_hdr_len
;
278 * When network is busy, some tcp options(like sack) will unpredictable
279 * occur in primary side or secondary side. it will make packet size
280 * not same, but the two packet's payload is identical. colo just
281 * care about packet payload, so we skip the option field.
283 res
= colo_packet_compare_common(ppkt
, spkt
, ptcp_offset
, stcp_offset
);
284 } else if (ptcp
->th_sum
== stcp
->th_sum
) {
285 res
= colo_packet_compare_common(ppkt
, spkt
, ETH_HLEN
, ETH_HLEN
);
291 trace_event_get_state_backends(TRACE_COLO_COMPARE_MISCOMPARE
)) {
292 char pri_ip_src
[20], pri_ip_dst
[20], sec_ip_src
[20], sec_ip_dst
[20];
294 strcpy(pri_ip_src
, inet_ntoa(ppkt
->ip
->ip_src
));
295 strcpy(pri_ip_dst
, inet_ntoa(ppkt
->ip
->ip_dst
));
296 strcpy(sec_ip_src
, inet_ntoa(spkt
->ip
->ip_src
));
297 strcpy(sec_ip_dst
, inet_ntoa(spkt
->ip
->ip_dst
));
299 trace_colo_compare_ip_info(ppkt
->size
, pri_ip_src
,
300 pri_ip_dst
, spkt
->size
,
301 sec_ip_src
, sec_ip_dst
);
303 trace_colo_compare_tcp_info("pri tcp packet",
309 trace_colo_compare_tcp_info("sec tcp packet",
315 qemu_hexdump((char *)ppkt
->data
, stderr
,
316 "colo-compare ppkt", ppkt
->size
);
317 qemu_hexdump((char *)spkt
->data
, stderr
,
318 "colo-compare spkt", spkt
->size
);
325 * Called from the compare thread on the primary
326 * for compare udp packet
328 static int colo_packet_compare_udp(Packet
*spkt
, Packet
*ppkt
)
331 int network_header_length
= ppkt
->ip
->ip_hl
* 4;
333 trace_colo_compare_main("compare udp");
336 * Because of ppkt and spkt are both in the same connection,
337 * The ppkt's src ip, dst ip, src port, dst port, ip_proto all are
338 * same with spkt. In addition, IP header's Identification is a random
339 * field, we can handle it in IP fragmentation function later.
340 * COLO just concern the response net packet payload from primary guest
341 * and secondary guest are same or not, So we ignored all IP header include
342 * other field like TOS,TTL,IP Checksum. we only need to compare
343 * the ip payload here.
345 ret
= colo_packet_compare_common(ppkt
, spkt
,
346 network_header_length
+ ETH_HLEN
,
347 network_header_length
+ ETH_HLEN
);
350 trace_colo_compare_udp_miscompare("primary pkt size", ppkt
->size
);
351 trace_colo_compare_udp_miscompare("Secondary pkt size", spkt
->size
);
352 if (trace_event_get_state_backends(TRACE_COLO_COMPARE_MISCOMPARE
)) {
353 qemu_hexdump((char *)ppkt
->data
, stderr
, "colo-compare pri pkt",
355 qemu_hexdump((char *)spkt
->data
, stderr
, "colo-compare sec pkt",
364 * Called from the compare thread on the primary
365 * for compare icmp packet
367 static int colo_packet_compare_icmp(Packet
*spkt
, Packet
*ppkt
)
369 int network_header_length
= ppkt
->ip
->ip_hl
* 4;
371 trace_colo_compare_main("compare icmp");
374 * Because of ppkt and spkt are both in the same connection,
375 * The ppkt's src ip, dst ip, src port, dst port, ip_proto all are
376 * same with spkt. In addition, IP header's Identification is a random
377 * field, we can handle it in IP fragmentation function later.
378 * COLO just concern the response net packet payload from primary guest
379 * and secondary guest are same or not, So we ignored all IP header include
380 * other field like TOS,TTL,IP Checksum. we only need to compare
381 * the ip payload here.
383 if (colo_packet_compare_common(ppkt
, spkt
,
384 network_header_length
+ ETH_HLEN
,
385 network_header_length
+ ETH_HLEN
)) {
386 trace_colo_compare_icmp_miscompare("primary pkt size",
388 trace_colo_compare_icmp_miscompare("Secondary pkt size",
390 if (trace_event_get_state_backends(TRACE_COLO_COMPARE_MISCOMPARE
)) {
391 qemu_hexdump((char *)ppkt
->data
, stderr
, "colo-compare pri pkt",
393 qemu_hexdump((char *)spkt
->data
, stderr
, "colo-compare sec pkt",
403 * Called from the compare thread on the primary
404 * for compare other packet
406 static int colo_packet_compare_other(Packet
*spkt
, Packet
*ppkt
)
408 trace_colo_compare_main("compare other");
409 if (trace_event_get_state_backends(TRACE_COLO_COMPARE_MISCOMPARE
)) {
410 char pri_ip_src
[20], pri_ip_dst
[20], sec_ip_src
[20], sec_ip_dst
[20];
412 strcpy(pri_ip_src
, inet_ntoa(ppkt
->ip
->ip_src
));
413 strcpy(pri_ip_dst
, inet_ntoa(ppkt
->ip
->ip_dst
));
414 strcpy(sec_ip_src
, inet_ntoa(spkt
->ip
->ip_src
));
415 strcpy(sec_ip_dst
, inet_ntoa(spkt
->ip
->ip_dst
));
417 trace_colo_compare_ip_info(ppkt
->size
, pri_ip_src
,
418 pri_ip_dst
, spkt
->size
,
419 sec_ip_src
, sec_ip_dst
);
422 return colo_packet_compare_common(ppkt
, spkt
, 0, 0);
425 static int colo_old_packet_check_one(Packet
*pkt
, int64_t *check_time
)
427 int64_t now
= qemu_clock_get_ms(QEMU_CLOCK_HOST
);
429 if ((now
- pkt
->creation_ms
) > (*check_time
)) {
430 trace_colo_old_packet_check_found(pkt
->creation_ms
);
437 static int colo_old_packet_check_one_conn(Connection
*conn
,
440 GList
*result
= NULL
;
441 int64_t check_time
= REGULAR_PACKET_CHECK_MS
;
443 result
= g_queue_find_custom(&conn
->primary_list
,
445 (GCompareFunc
)colo_old_packet_check_one
);
448 /* Do checkpoint will flush old packet */
450 * TODO: Notify colo frame to do checkpoint.
451 * colo_compare_inconsistent_notify();
460 * Look for old packets that the secondary hasn't matched,
461 * if we have some then we have to checkpoint to wake
464 static void colo_old_packet_check(void *opaque
)
466 CompareState
*s
= opaque
;
469 * If we find one old packet, stop finding job and notify
470 * COLO frame do checkpoint.
472 g_queue_find_custom(&s
->conn_list
, NULL
,
473 (GCompareFunc
)colo_old_packet_check_one_conn
);
477 * Called from the compare thread on the primary
478 * for compare connection
480 static void colo_compare_connection(void *opaque
, void *user_data
)
482 CompareState
*s
= user_data
;
483 Connection
*conn
= opaque
;
485 GList
*result
= NULL
;
488 while (!g_queue_is_empty(&conn
->primary_list
) &&
489 !g_queue_is_empty(&conn
->secondary_list
)) {
490 pkt
= g_queue_pop_head(&conn
->primary_list
);
491 switch (conn
->ip_proto
) {
493 result
= g_queue_find_custom(&conn
->secondary_list
,
494 pkt
, (GCompareFunc
)colo_packet_compare_tcp
);
497 result
= g_queue_find_custom(&conn
->secondary_list
,
498 pkt
, (GCompareFunc
)colo_packet_compare_udp
);
501 result
= g_queue_find_custom(&conn
->secondary_list
,
502 pkt
, (GCompareFunc
)colo_packet_compare_icmp
);
505 result
= g_queue_find_custom(&conn
->secondary_list
,
506 pkt
, (GCompareFunc
)colo_packet_compare_other
);
511 ret
= compare_chr_send(s
,
516 error_report("colo_send_primary_packet failed");
518 trace_colo_compare_main("packet same and release packet");
519 g_queue_remove(&conn
->secondary_list
, result
->data
);
520 packet_destroy(pkt
, NULL
);
523 * If one packet arrive late, the secondary_list or
524 * primary_list will be empty, so we can't compare it
525 * until next comparison.
527 trace_colo_compare_main("packet different");
528 g_queue_push_head(&conn
->primary_list
, pkt
);
529 /* TODO: colo_notify_checkpoint();*/
535 static int compare_chr_send(CompareState
*s
,
538 uint32_t vnet_hdr_len
)
541 uint32_t len
= htonl(size
);
547 ret
= qemu_chr_fe_write_all(&s
->chr_out
, (uint8_t *)&len
, sizeof(len
));
548 if (ret
!= sizeof(len
)) {
554 * We send vnet header len make other module(like filter-redirector)
555 * know how to parse net packet correctly.
557 len
= htonl(vnet_hdr_len
);
558 ret
= qemu_chr_fe_write_all(&s
->chr_out
, (uint8_t *)&len
, sizeof(len
));
559 if (ret
!= sizeof(len
)) {
564 ret
= qemu_chr_fe_write_all(&s
->chr_out
, (uint8_t *)buf
, size
);
572 return ret
< 0 ? ret
: -EIO
;
575 static int compare_chr_can_read(void *opaque
)
577 return COMPARE_READ_LEN_MAX
;
581 * Called from the main thread on the primary for packets
582 * arriving over the socket from the primary.
584 static void compare_pri_chr_in(void *opaque
, const uint8_t *buf
, int size
)
586 CompareState
*s
= COLO_COMPARE(opaque
);
589 ret
= net_fill_rstate(&s
->pri_rs
, buf
, size
);
591 qemu_chr_fe_set_handlers(&s
->chr_pri_in
, NULL
, NULL
, NULL
, NULL
,
593 error_report("colo-compare primary_in error");
598 * Called from the main thread on the primary for packets
599 * arriving over the socket from the secondary.
601 static void compare_sec_chr_in(void *opaque
, const uint8_t *buf
, int size
)
603 CompareState
*s
= COLO_COMPARE(opaque
);
606 ret
= net_fill_rstate(&s
->sec_rs
, buf
, size
);
608 qemu_chr_fe_set_handlers(&s
->chr_sec_in
, NULL
, NULL
, NULL
, NULL
,
610 error_report("colo-compare secondary_in error");
615 * Check old packet regularly so it can watch for any packets
616 * that the secondary hasn't produced equivalents of.
618 static void check_old_packet_regular(void *opaque
)
620 CompareState
*s
= opaque
;
622 /* if have old packet we will notify checkpoint */
623 colo_old_packet_check(s
);
624 timer_mod(s
->packet_check_timer
, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL
) +
625 REGULAR_PACKET_CHECK_MS
);
628 static void colo_compare_timer_init(CompareState
*s
)
630 AioContext
*ctx
= iothread_get_aio_context(s
->iothread
);
632 s
->packet_check_timer
= aio_timer_new(ctx
, QEMU_CLOCK_VIRTUAL
,
633 SCALE_MS
, check_old_packet_regular
,
635 timer_mod(s
->packet_check_timer
, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL
) +
636 REGULAR_PACKET_CHECK_MS
);
639 static void colo_compare_timer_del(CompareState
*s
)
641 if (s
->packet_check_timer
) {
642 timer_del(s
->packet_check_timer
);
643 timer_free(s
->packet_check_timer
);
644 s
->packet_check_timer
= NULL
;
648 static void colo_compare_iothread(CompareState
*s
)
650 object_ref(OBJECT(s
->iothread
));
651 s
->worker_context
= iothread_get_g_main_context(s
->iothread
);
653 qemu_chr_fe_set_handlers(&s
->chr_pri_in
, compare_chr_can_read
,
654 compare_pri_chr_in
, NULL
, NULL
,
655 s
, s
->worker_context
, true);
656 qemu_chr_fe_set_handlers(&s
->chr_sec_in
, compare_chr_can_read
,
657 compare_sec_chr_in
, NULL
, NULL
,
658 s
, s
->worker_context
, true);
660 colo_compare_timer_init(s
);
663 static char *compare_get_pri_indev(Object
*obj
, Error
**errp
)
665 CompareState
*s
= COLO_COMPARE(obj
);
667 return g_strdup(s
->pri_indev
);
670 static void compare_set_pri_indev(Object
*obj
, const char *value
, Error
**errp
)
672 CompareState
*s
= COLO_COMPARE(obj
);
674 g_free(s
->pri_indev
);
675 s
->pri_indev
= g_strdup(value
);
678 static char *compare_get_sec_indev(Object
*obj
, Error
**errp
)
680 CompareState
*s
= COLO_COMPARE(obj
);
682 return g_strdup(s
->sec_indev
);
685 static void compare_set_sec_indev(Object
*obj
, const char *value
, Error
**errp
)
687 CompareState
*s
= COLO_COMPARE(obj
);
689 g_free(s
->sec_indev
);
690 s
->sec_indev
= g_strdup(value
);
693 static char *compare_get_outdev(Object
*obj
, Error
**errp
)
695 CompareState
*s
= COLO_COMPARE(obj
);
697 return g_strdup(s
->outdev
);
700 static void compare_set_outdev(Object
*obj
, const char *value
, Error
**errp
)
702 CompareState
*s
= COLO_COMPARE(obj
);
705 s
->outdev
= g_strdup(value
);
708 static bool compare_get_vnet_hdr(Object
*obj
, Error
**errp
)
710 CompareState
*s
= COLO_COMPARE(obj
);
715 static void compare_set_vnet_hdr(Object
*obj
,
719 CompareState
*s
= COLO_COMPARE(obj
);
724 static void compare_pri_rs_finalize(SocketReadState
*pri_rs
)
726 CompareState
*s
= container_of(pri_rs
, CompareState
, pri_rs
);
728 if (packet_enqueue(s
, PRIMARY_IN
)) {
729 trace_colo_compare_main("primary: unsupported packet in");
733 pri_rs
->vnet_hdr_len
);
735 /* compare connection */
736 g_queue_foreach(&s
->conn_list
, colo_compare_connection
, s
);
740 static void compare_sec_rs_finalize(SocketReadState
*sec_rs
)
742 CompareState
*s
= container_of(sec_rs
, CompareState
, sec_rs
);
744 if (packet_enqueue(s
, SECONDARY_IN
)) {
745 trace_colo_compare_main("secondary: unsupported packet in");
747 /* compare connection */
748 g_queue_foreach(&s
->conn_list
, colo_compare_connection
, s
);
754 * Return 0 is success.
755 * Return 1 is failed.
757 static int find_and_check_chardev(Chardev
**chr
,
761 *chr
= qemu_chr_find(chr_name
);
763 error_setg(errp
, "Device '%s' not found",
768 if (!qemu_chr_has_feature(*chr
, QEMU_CHAR_FEATURE_RECONNECTABLE
)) {
769 error_setg(errp
, "chardev \"%s\" is not reconnectable",
778 * Called from the main thread on the primary
779 * to setup colo-compare.
781 static void colo_compare_complete(UserCreatable
*uc
, Error
**errp
)
783 CompareState
*s
= COLO_COMPARE(uc
);
786 if (!s
->pri_indev
|| !s
->sec_indev
|| !s
->outdev
|| !s
->iothread
) {
787 error_setg(errp
, "colo compare needs 'primary_in' ,"
788 "'secondary_in','outdev','iothread' property set");
790 } else if (!strcmp(s
->pri_indev
, s
->outdev
) ||
791 !strcmp(s
->sec_indev
, s
->outdev
) ||
792 !strcmp(s
->pri_indev
, s
->sec_indev
)) {
793 error_setg(errp
, "'indev' and 'outdev' could not be same "
794 "for compare module");
798 if (find_and_check_chardev(&chr
, s
->pri_indev
, errp
) ||
799 !qemu_chr_fe_init(&s
->chr_pri_in
, chr
, errp
)) {
803 if (find_and_check_chardev(&chr
, s
->sec_indev
, errp
) ||
804 !qemu_chr_fe_init(&s
->chr_sec_in
, chr
, errp
)) {
808 if (find_and_check_chardev(&chr
, s
->outdev
, errp
) ||
809 !qemu_chr_fe_init(&s
->chr_out
, chr
, errp
)) {
813 net_socket_rs_init(&s
->pri_rs
, compare_pri_rs_finalize
, s
->vnet_hdr
);
814 net_socket_rs_init(&s
->sec_rs
, compare_sec_rs_finalize
, s
->vnet_hdr
);
816 g_queue_init(&s
->conn_list
);
818 s
->connection_track_table
= g_hash_table_new_full(connection_key_hash
,
819 connection_key_equal
,
823 colo_compare_iothread(s
);
827 static void colo_flush_packets(void *opaque
, void *user_data
)
829 CompareState
*s
= user_data
;
830 Connection
*conn
= opaque
;
833 while (!g_queue_is_empty(&conn
->primary_list
)) {
834 pkt
= g_queue_pop_head(&conn
->primary_list
);
839 packet_destroy(pkt
, NULL
);
841 while (!g_queue_is_empty(&conn
->secondary_list
)) {
842 pkt
= g_queue_pop_head(&conn
->secondary_list
);
843 packet_destroy(pkt
, NULL
);
847 static void colo_compare_class_init(ObjectClass
*oc
, void *data
)
849 UserCreatableClass
*ucc
= USER_CREATABLE_CLASS(oc
);
851 ucc
->complete
= colo_compare_complete
;
854 static void colo_compare_init(Object
*obj
)
856 CompareState
*s
= COLO_COMPARE(obj
);
858 object_property_add_str(obj
, "primary_in",
859 compare_get_pri_indev
, compare_set_pri_indev
,
861 object_property_add_str(obj
, "secondary_in",
862 compare_get_sec_indev
, compare_set_sec_indev
,
864 object_property_add_str(obj
, "outdev",
865 compare_get_outdev
, compare_set_outdev
,
867 object_property_add_link(obj
, "iothread", TYPE_IOTHREAD
,
868 (Object
**)&s
->iothread
,
869 object_property_allow_set_link
,
870 OBJ_PROP_LINK_UNREF_ON_RELEASE
, NULL
);
873 object_property_add_bool(obj
, "vnet_hdr_support", compare_get_vnet_hdr
,
874 compare_set_vnet_hdr
, NULL
);
877 static void colo_compare_finalize(Object
*obj
)
879 CompareState
*s
= COLO_COMPARE(obj
);
881 qemu_chr_fe_deinit(&s
->chr_pri_in
, false);
882 qemu_chr_fe_deinit(&s
->chr_sec_in
, false);
883 qemu_chr_fe_deinit(&s
->chr_out
, false);
885 colo_compare_timer_del(s
);
887 /* Release all unhandled packets after compare thead exited */
888 g_queue_foreach(&s
->conn_list
, colo_flush_packets
, s
);
890 g_queue_clear(&s
->conn_list
);
892 if (s
->connection_track_table
) {
893 g_hash_table_destroy(s
->connection_track_table
);
897 object_unref(OBJECT(s
->iothread
));
899 g_free(s
->pri_indev
);
900 g_free(s
->sec_indev
);
904 static const TypeInfo colo_compare_info
= {
905 .name
= TYPE_COLO_COMPARE
,
906 .parent
= TYPE_OBJECT
,
907 .instance_size
= sizeof(CompareState
),
908 .instance_init
= colo_compare_init
,
909 .instance_finalize
= colo_compare_finalize
,
910 .class_size
= sizeof(CompareClass
),
911 .class_init
= colo_compare_class_init
,
912 .interfaces
= (InterfaceInfo
[]) {
913 { TYPE_USER_CREATABLE
},
918 static void register_types(void)
920 type_register_static(&colo_compare_info
);
923 type_init(register_types
);