-----BEGIN PGP SIGNATURE-----

Version: GnuPG v1
 
 iQEcBAABAgAGBQJdGr/CAAoJEO8Ells5jWIRJY4H/10pMuRX11WDSQqc/ejQMywj
 3v+CBFTaNnCvbPqEFKH6p/1Y1KV/G1+ODzuat71QVjHqzlX/tCcLtNVrzIsozal+
 R42PADVpBuOGYC9CBBKZxKpORm4YEOApwERKAmjoTDKJUbho1TOOt60Vz7ctlybb
 nlEDdNogSKZ7ZxSj/Nn5NvaZnQPpS3ZpFcqMwr1ofQtCuUsYi9DNREd+NLwaknZS
 O3ib6nAFxBH4dpA0bSKWJHZi8QU1GcIdvs0iDeSnzvlCgwjbv3pM6eYkse90jXq+
 Sq0ZxSMb+97CCu54b5wu8EqOhzBaasVcKApR3NRQc5vHBI/1WMNB5eFWmfSpzWM=
 =3+Zi
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/jasowang/tags/net-pull-request' into staging

# gpg: Signature made Tue 02 Jul 2019 03:21:54 BST
# gpg:                using RSA key EF04965B398D6211
# gpg: Good signature from "Jason Wang (Jason Wang on RedHat) <jasowang@redhat.com>" [marginal]
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 215D 46F4 8246 689E C77F  3562 EF04 965B 398D 6211

* remotes/jasowang/tags/net-pull-request:
  migration/colo.c: Add missed filter notify for Xen COLO.
  COLO-compare: Add colo-compare remote notify support
  COLO-compare: Make the compare_chr_send() can send notification message.
  COLO-compare: Add remote notification chardev handler frame
  COLO-compare: Add new parameter to communicate with remote colo-frame
  net/announce: Expand test for stopping self announce
  net/announce: Add HMP optional ID
  net/announce: Add optional ID
  net/announce: Add HMP optional interface list
  net/announce: Allow optional list of interfaces
  net: remove unused get_str_sep() function
  net: use g_strsplit() for parsing host address and port
  net: avoid using variable length array in net_client_init()
  net: fix assertion failure when ipv6-prefixlen is not a number
  ftgmac100: do not link to netdev
  qemu-bridge-helper: Document known shortcomings
  MAINTAINERS: Add qemu-bridge-helper.c to "Network device backends"

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-07-02 16:41:28 +01:00
commit 8ef53cdb50
15 changed files with 430 additions and 99 deletions

View file

@ -1944,6 +1944,7 @@ M: Jason Wang <jasowang@redhat.com>
S: Maintained S: Maintained
F: net/ F: net/
F: include/net/ F: include/net/
F: qemu-bridge-helper.c
T: git https://github.com/jasowang/qemu.git net T: git https://github.com/jasowang/qemu.git net
F: qapi/net.json F: qapi/net.json

View file

@ -955,8 +955,8 @@ ETEXI
{ {
.name = "announce_self", .name = "announce_self",
.args_type = "", .args_type = "interfaces:s?,id:s?",
.params = "", .params = "[interfaces] [id]",
.help = "Trigger GARP/RARP announcements", .help = "Trigger GARP/RARP announcements",
.cmd = hmp_announce_self, .cmd = hmp_announce_self,
}, },
@ -967,6 +967,9 @@ STEXI
Trigger a round of GARP/RARP broadcasts; this is useful for explicitly updating the Trigger a round of GARP/RARP broadcasts; this is useful for explicitly updating the
network infrastructure after a reconfiguration or some forms of migration. network infrastructure after a reconfiguration or some forms of migration.
The timings of the round are set by the migration announce parameters. The timings of the round are set by the migration announce parameters.
An optional comma separated @var{interfaces} list restricts the announce to the
named set of interfaces. An optional @var{id} can be used to start a separate announce
timer and to change the parameters of it later.
ETEXI ETEXI
{ {

View file

@ -1017,8 +1017,6 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp)
sysbus_init_irq(sbd, &s->irq); sysbus_init_irq(sbd, &s->irq);
qemu_macaddr_default_if_unset(&s->conf.macaddr); qemu_macaddr_default_if_unset(&s->conf.macaddr);
s->conf.peers.ncs[0] = nd_table[0].netdev;
s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf, s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf,
object_get_typename(OBJECT(dev)), DEVICE(dev)->id, object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
s); s);

View file

@ -2360,7 +2360,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
timer_mod(n->announce_timer.tm, timer_mod(n->announce_timer.tm,
qemu_clock_get_ms(n->announce_timer.type)); qemu_clock_get_ms(n->announce_timer.type));
} else { } else {
qemu_announce_timer_del(&n->announce_timer); qemu_announce_timer_del(&n->announce_timer, false);
} }
} }
@ -2784,7 +2784,7 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
virtio_net_del_queue(n, i); virtio_net_del_queue(n, i);
} }
qemu_announce_timer_del(&n->announce_timer); qemu_announce_timer_del(&n->announce_timer, false);
g_free(n->vqs); g_free(n->vqs);
qemu_del_nic(n->nic); qemu_del_nic(n->nic);
virtio_net_rsc_cleanup(n); virtio_net_rsc_cleanup(n);

View file

@ -22,8 +22,12 @@ struct AnnounceTimer {
/* Returns: update the timer to the next time point */ /* Returns: update the timer to the next time point */
int64_t qemu_announce_timer_step(AnnounceTimer *timer); int64_t qemu_announce_timer_step(AnnounceTimer *timer);
/* Delete the underlying timer */ /*
void qemu_announce_timer_del(AnnounceTimer *timer); * Delete the underlying timer and other data
* If 'free_named' true and the timer is a named timer, then remove
* it from the list of named timers and free the AnnounceTimer itself.
*/
void qemu_announce_timer_del(AnnounceTimer *timer, bool free_named);
/* /*
* Under BQL/main thread * Under BQL/main thread

View file

@ -259,6 +259,8 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
void qmp_xen_colo_do_checkpoint(Error **errp) void qmp_xen_colo_do_checkpoint(Error **errp)
{ {
replication_do_checkpoint_all(errp); replication_do_checkpoint_all(errp);
/* Notify all filters of all NIC to do checkpoint */
colo_notify_filters_event(COLO_EVENT_CHECKPOINT, errp);
} }
#endif #endif

View file

@ -27,6 +27,7 @@
#include "monitor/monitor-internal.h" #include "monitor/monitor-internal.h"
#include "monitor/qdev.h" #include "monitor/qdev.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/clone-visitor.h"
#include "qapi/opts-visitor.h" #include "qapi/opts-visitor.h"
#include "qapi/qapi-builtin-visit.h" #include "qapi/qapi-builtin-visit.h"
#include "qapi/qapi-commands-block.h" #include "qapi/qapi-commands-block.h"
@ -38,6 +39,7 @@
#include "qapi/qapi-commands-run-state.h" #include "qapi/qapi-commands-run-state.h"
#include "qapi/qapi-commands-tpm.h" #include "qapi/qapi-commands-tpm.h"
#include "qapi/qapi-commands-ui.h" #include "qapi/qapi-commands-ui.h"
#include "qapi/qapi-visit-net.h"
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qapi/string-input-visitor.h" #include "qapi/string-input-visitor.h"
@ -67,6 +69,32 @@ static void hmp_handle_error(Monitor *mon, Error **errp)
} }
} }
/*
* Produce a strList from a comma separated list.
* A NULL or empty input string return NULL.
*/
static strList *strList_from_comma_list(const char *in)
{
strList *res = NULL;
strList **hook = &res;
while (in && in[0]) {
char *comma = strchr(in, ',');
*hook = g_new0(strList, 1);
if (comma) {
(*hook)->value = g_strndup(in, comma - in);
in = comma + 1; /* skip the , */
} else {
(*hook)->value = g_strdup(in);
in = NULL;
}
hook = &(*hook)->next;
}
return res;
}
void hmp_info_name(Monitor *mon, const QDict *qdict) void hmp_info_name(Monitor *mon, const QDict *qdict)
{ {
NameInfo *info; NameInfo *info;
@ -1631,7 +1659,18 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict)
void hmp_announce_self(Monitor *mon, const QDict *qdict) void hmp_announce_self(Monitor *mon, const QDict *qdict)
{ {
qmp_announce_self(migrate_announce_params(), NULL); const char *interfaces_str = qdict_get_try_str(qdict, "interfaces");
const char *id = qdict_get_try_str(qdict, "id");
AnnounceParameters *params = QAPI_CLONE(AnnounceParameters,
migrate_announce_params());
qapi_free_strList(params->interfaces);
params->interfaces = strList_from_comma_list(interfaces_str);
params->has_interfaces = params->interfaces != NULL;
params->id = g_strdup(id);
params->has_id = !!params->id;
qmp_announce_self(params, NULL);
qapi_free_AnnounceParameters(params);
} }
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict) void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)

View file

@ -15,6 +15,8 @@
#include "qapi/qapi-commands-net.h" #include "qapi/qapi-commands-net.h"
#include "trace.h" #include "trace.h"
static GData *named_timers;
int64_t qemu_announce_timer_step(AnnounceTimer *timer) int64_t qemu_announce_timer_step(AnnounceTimer *timer)
{ {
int64_t step; int64_t step;
@ -31,13 +33,38 @@ int64_t qemu_announce_timer_step(AnnounceTimer *timer)
return step; return step;
} }
void qemu_announce_timer_del(AnnounceTimer *timer) /*
* If 'free_named' is true, then remove the timer from the list
* and free the timer itself.
*/
void qemu_announce_timer_del(AnnounceTimer *timer, bool free_named)
{ {
bool free_timer = false;
if (timer->tm) { if (timer->tm) {
timer_del(timer->tm); timer_del(timer->tm);
timer_free(timer->tm); timer_free(timer->tm);
timer->tm = NULL; timer->tm = NULL;
} }
qapi_free_strList(timer->params.interfaces);
timer->params.interfaces = NULL;
if (free_named && timer->params.has_id) {
AnnounceTimer *list_timer;
/*
* Sanity check: There should only be one timer on the list with
* the id.
*/
list_timer = g_datalist_get_data(&named_timers, timer->params.id);
assert(timer == list_timer);
free_timer = true;
g_datalist_remove_data(&named_timers, timer->params.id);
}
trace_qemu_announce_timer_del(free_named, free_timer, timer->params.id);
g_free(timer->params.id);
timer->params.id = NULL;
if (free_timer) {
g_free(timer);
}
} }
/* /*
@ -54,7 +81,7 @@ void qemu_announce_timer_reset(AnnounceTimer *timer,
* We're under the BQL, so the current timer can't * We're under the BQL, so the current timer can't
* be firing, so we should be able to delete it. * be firing, so we should be able to delete it.
*/ */
qemu_announce_timer_del(timer); qemu_announce_timer_del(timer, false);
QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params); QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params);
timer->round = params->rounds; timer->round = params->rounds;
@ -96,29 +123,53 @@ static int announce_self_create(uint8_t *buf,
static void qemu_announce_self_iter(NICState *nic, void *opaque) static void qemu_announce_self_iter(NICState *nic, void *opaque)
{ {
AnnounceTimer *timer = opaque;
uint8_t buf[60]; uint8_t buf[60];
int len; int len;
bool skip;
trace_qemu_announce_self_iter(qemu_ether_ntoa(&nic->conf->macaddr)); if (timer->params.has_interfaces) {
len = announce_self_create(buf, nic->conf->macaddr.a); strList *entry = timer->params.interfaces;
/* Skip unless we find our name in the requested list */
skip = true;
qemu_send_packet_raw(qemu_get_queue(nic), buf, len); while (entry) {
if (!strcmp(entry->value, nic->ncs->name)) {
/* Found us */
skip = false;
break;
}
entry = entry->next;
}
} else {
skip = false;
}
/* if the NIC provides it's own announcement support, use it as well */ trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_",
if (nic->ncs->info->announce) { nic->ncs->name,
nic->ncs->info->announce(nic->ncs); qemu_ether_ntoa(&nic->conf->macaddr), skip);
if (!skip) {
len = announce_self_create(buf, nic->conf->macaddr.a);
qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
/* if the NIC provides it's own announcement support, use it as well */
if (nic->ncs->info->announce) {
nic->ncs->info->announce(nic->ncs);
}
} }
} }
static void qemu_announce_self_once(void *opaque) static void qemu_announce_self_once(void *opaque)
{ {
AnnounceTimer *timer = (AnnounceTimer *)opaque; AnnounceTimer *timer = (AnnounceTimer *)opaque;
qemu_foreach_nic(qemu_announce_self_iter, NULL); qemu_foreach_nic(qemu_announce_self_iter, timer);
if (--timer->round) { if (--timer->round) {
qemu_announce_timer_step(timer); qemu_announce_timer_step(timer);
} else { } else {
qemu_announce_timer_del(timer); qemu_announce_timer_del(timer, true);
} }
} }
@ -129,12 +180,24 @@ void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params)
if (params->rounds) { if (params->rounds) {
qemu_announce_self_once(timer); qemu_announce_self_once(timer);
} else { } else {
qemu_announce_timer_del(timer); qemu_announce_timer_del(timer, true);
} }
} }
void qmp_announce_self(AnnounceParameters *params, Error **errp) void qmp_announce_self(AnnounceParameters *params, Error **errp)
{ {
static AnnounceTimer announce_timer; AnnounceTimer *named_timer;
qemu_announce_self(&announce_timer, params); if (!params->has_id) {
params->id = g_strdup("");
params->has_id = true;
}
named_timer = g_datalist_get_data(&named_timers, params->id);
if (!named_timer) {
named_timer = g_new0(AnnounceTimer, 1);
g_datalist_set_data(&named_timers, params->id, named_timer);
}
qemu_announce_self(named_timer, params);
} }

View file

@ -83,11 +83,14 @@ typedef struct CompareState {
char *pri_indev; char *pri_indev;
char *sec_indev; char *sec_indev;
char *outdev; char *outdev;
char *notify_dev;
CharBackend chr_pri_in; CharBackend chr_pri_in;
CharBackend chr_sec_in; CharBackend chr_sec_in;
CharBackend chr_out; CharBackend chr_out;
CharBackend chr_notify_dev;
SocketReadState pri_rs; SocketReadState pri_rs;
SocketReadState sec_rs; SocketReadState sec_rs;
SocketReadState notify_rs;
bool vnet_hdr; bool vnet_hdr;
/* /*
@ -117,16 +120,33 @@ enum {
SECONDARY_IN, SECONDARY_IN,
}; };
static void colo_compare_inconsistency_notify(void)
{
notifier_list_notify(&colo_compare_notifiers,
migrate_get_current());
}
static int compare_chr_send(CompareState *s, static int compare_chr_send(CompareState *s,
const uint8_t *buf, const uint8_t *buf,
uint32_t size, uint32_t size,
uint32_t vnet_hdr_len); uint32_t vnet_hdr_len,
bool notify_remote_frame);
static void notify_remote_frame(CompareState *s)
{
char msg[] = "DO_CHECKPOINT";
int ret = 0;
ret = compare_chr_send(s, (uint8_t *)msg, strlen(msg), 0, true);
if (ret < 0) {
error_report("Notify Xen COLO-frame failed");
}
}
static void colo_compare_inconsistency_notify(CompareState *s)
{
if (s->notify_dev) {
notify_remote_frame(s);
} else {
notifier_list_notify(&colo_compare_notifiers,
migrate_get_current());
}
}
static gint seq_sorter(Packet *a, Packet *b, gpointer data) static gint seq_sorter(Packet *a, Packet *b, gpointer data)
{ {
@ -238,7 +258,8 @@ static void colo_release_primary_pkt(CompareState *s, Packet *pkt)
ret = compare_chr_send(s, ret = compare_chr_send(s,
pkt->data, pkt->data,
pkt->size, pkt->size,
pkt->vnet_hdr_len); pkt->vnet_hdr_len,
false);
if (ret < 0) { if (ret < 0) {
error_report("colo send primary packet failed"); error_report("colo send primary packet failed");
} }
@ -430,7 +451,7 @@ sec:
qemu_hexdump((char *)spkt->data, stderr, qemu_hexdump((char *)spkt->data, stderr,
"colo-compare spkt", spkt->size); "colo-compare spkt", spkt->size);
colo_compare_inconsistency_notify(); colo_compare_inconsistency_notify(s);
} }
} }
@ -572,7 +593,7 @@ void colo_compare_unregister_notifier(Notifier *notify)
} }
static int colo_old_packet_check_one_conn(Connection *conn, static int colo_old_packet_check_one_conn(Connection *conn,
void *user_data) CompareState *s)
{ {
GList *result = NULL; GList *result = NULL;
int64_t check_time = REGULAR_PACKET_CHECK_MS; int64_t check_time = REGULAR_PACKET_CHECK_MS;
@ -583,7 +604,7 @@ static int colo_old_packet_check_one_conn(Connection *conn,
if (result) { if (result) {
/* Do checkpoint will flush old packet */ /* Do checkpoint will flush old packet */
colo_compare_inconsistency_notify(); colo_compare_inconsistency_notify(s);
return 0; return 0;
} }
@ -603,7 +624,7 @@ static void colo_old_packet_check(void *opaque)
* If we find one old packet, stop finding job and notify * If we find one old packet, stop finding job and notify
* COLO frame do checkpoint. * COLO frame do checkpoint.
*/ */
g_queue_find_custom(&s->conn_list, NULL, g_queue_find_custom(&s->conn_list, s,
(GCompareFunc)colo_old_packet_check_one_conn); (GCompareFunc)colo_old_packet_check_one_conn);
} }
@ -632,7 +653,8 @@ static void colo_compare_packet(CompareState *s, Connection *conn,
*/ */
trace_colo_compare_main("packet different"); trace_colo_compare_main("packet different");
g_queue_push_head(&conn->primary_list, pkt); g_queue_push_head(&conn->primary_list, pkt);
colo_compare_inconsistency_notify();
colo_compare_inconsistency_notify(s);
break; break;
} }
} }
@ -668,7 +690,8 @@ static void colo_compare_connection(void *opaque, void *user_data)
static int compare_chr_send(CompareState *s, static int compare_chr_send(CompareState *s,
const uint8_t *buf, const uint8_t *buf,
uint32_t size, uint32_t size,
uint32_t vnet_hdr_len) uint32_t vnet_hdr_len,
bool notify_remote_frame)
{ {
int ret = 0; int ret = 0;
uint32_t len = htonl(size); uint32_t len = htonl(size);
@ -677,7 +700,14 @@ static int compare_chr_send(CompareState *s,
return 0; return 0;
} }
ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len)); if (notify_remote_frame) {
ret = qemu_chr_fe_write_all(&s->chr_notify_dev,
(uint8_t *)&len,
sizeof(len));
} else {
ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len));
}
if (ret != sizeof(len)) { if (ret != sizeof(len)) {
goto err; goto err;
} }
@ -688,13 +718,26 @@ static int compare_chr_send(CompareState *s,
* know how to parse net packet correctly. * know how to parse net packet correctly.
*/ */
len = htonl(vnet_hdr_len); len = htonl(vnet_hdr_len);
ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len));
if (!notify_remote_frame) {
ret = qemu_chr_fe_write_all(&s->chr_out,
(uint8_t *)&len,
sizeof(len));
}
if (ret != sizeof(len)) { if (ret != sizeof(len)) {
goto err; goto err;
} }
} }
ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)buf, size); if (notify_remote_frame) {
ret = qemu_chr_fe_write_all(&s->chr_notify_dev,
(uint8_t *)buf,
size);
} else {
ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)buf, size);
}
if (ret != size) { if (ret != size) {
goto err; goto err;
} }
@ -744,6 +787,19 @@ static void compare_sec_chr_in(void *opaque, const uint8_t *buf, int size)
} }
} }
static void compare_notify_chr(void *opaque, const uint8_t *buf, int size)
{
CompareState *s = COLO_COMPARE(opaque);
int ret;
ret = net_fill_rstate(&s->notify_rs, buf, size);
if (ret == -1) {
qemu_chr_fe_set_handlers(&s->chr_notify_dev, NULL, NULL, NULL, NULL,
NULL, NULL, true);
error_report("colo-compare notify_dev error");
}
}
/* /*
* Check old packet regularly so it can watch for any packets * Check old packet regularly so it can watch for any packets
* that the secondary hasn't produced equivalents of. * that the secondary hasn't produced equivalents of.
@ -831,6 +887,11 @@ static void colo_compare_iothread(CompareState *s)
qemu_chr_fe_set_handlers(&s->chr_sec_in, compare_chr_can_read, qemu_chr_fe_set_handlers(&s->chr_sec_in, compare_chr_can_read,
compare_sec_chr_in, NULL, NULL, compare_sec_chr_in, NULL, NULL,
s, s->worker_context, true); s, s->worker_context, true);
if (s->notify_dev) {
qemu_chr_fe_set_handlers(&s->chr_notify_dev, compare_chr_can_read,
compare_notify_chr, NULL, NULL,
s, s->worker_context, true);
}
colo_compare_timer_init(s); colo_compare_timer_init(s);
s->event_bh = qemu_bh_new(colo_compare_handle_event, s); s->event_bh = qemu_bh_new(colo_compare_handle_event, s);
@ -897,6 +958,21 @@ static void compare_set_vnet_hdr(Object *obj,
s->vnet_hdr = value; s->vnet_hdr = value;
} }
static char *compare_get_notify_dev(Object *obj, Error **errp)
{
CompareState *s = COLO_COMPARE(obj);
return g_strdup(s->notify_dev);
}
static void compare_set_notify_dev(Object *obj, const char *value, Error **errp)
{
CompareState *s = COLO_COMPARE(obj);
g_free(s->notify_dev);
s->notify_dev = g_strdup(value);
}
static void compare_pri_rs_finalize(SocketReadState *pri_rs) static void compare_pri_rs_finalize(SocketReadState *pri_rs)
{ {
CompareState *s = container_of(pri_rs, CompareState, pri_rs); CompareState *s = container_of(pri_rs, CompareState, pri_rs);
@ -907,7 +983,8 @@ static void compare_pri_rs_finalize(SocketReadState *pri_rs)
compare_chr_send(s, compare_chr_send(s,
pri_rs->buf, pri_rs->buf,
pri_rs->packet_len, pri_rs->packet_len,
pri_rs->vnet_hdr_len); pri_rs->vnet_hdr_len,
false);
} else { } else {
/* compare packet in the specified connection */ /* compare packet in the specified connection */
colo_compare_connection(conn, s); colo_compare_connection(conn, s);
@ -927,6 +1004,27 @@ static void compare_sec_rs_finalize(SocketReadState *sec_rs)
} }
} }
static void compare_notify_rs_finalize(SocketReadState *notify_rs)
{
CompareState *s = container_of(notify_rs, CompareState, notify_rs);
/* Get Xen colo-frame's notify and handle the message */
char *data = g_memdup(notify_rs->buf, notify_rs->packet_len);
char msg[] = "COLO_COMPARE_GET_XEN_INIT";
int ret;
if (!strcmp(data, "COLO_USERSPACE_PROXY_INIT")) {
ret = compare_chr_send(s, (uint8_t *)msg, strlen(msg), 0, true);
if (ret < 0) {
error_report("Notify Xen COLO-frame INIT failed");
}
}
if (!strcmp(data, "COLO_CHECKPOINT")) {
/* colo-compare do checkpoint, flush pri packet and remove sec packet */
g_queue_foreach(&s->conn_list, colo_flush_packets, s);
}
}
/* /*
* Return 0 is success. * Return 0 is success.
@ -997,6 +1095,17 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp)
net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize, s->vnet_hdr); net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize, s->vnet_hdr);
net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize, s->vnet_hdr); net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize, s->vnet_hdr);
/* Try to enable remote notify chardev, currently just for Xen COLO */
if (s->notify_dev) {
if (find_and_check_chardev(&chr, s->notify_dev, errp) ||
!qemu_chr_fe_init(&s->chr_notify_dev, chr, errp)) {
return;
}
net_socket_rs_init(&s->notify_rs, compare_notify_rs_finalize,
s->vnet_hdr);
}
QTAILQ_INSERT_TAIL(&net_compares, s, next); QTAILQ_INSERT_TAIL(&net_compares, s, next);
g_queue_init(&s->conn_list); g_queue_init(&s->conn_list);
@ -1024,7 +1133,8 @@ static void colo_flush_packets(void *opaque, void *user_data)
compare_chr_send(s, compare_chr_send(s,
pkt->data, pkt->data,
pkt->size, pkt->size,
pkt->vnet_hdr_len); pkt->vnet_hdr_len,
false);
packet_destroy(pkt, NULL); packet_destroy(pkt, NULL);
} }
while (!g_queue_is_empty(&conn->secondary_list)) { while (!g_queue_is_empty(&conn->secondary_list)) {
@ -1057,6 +1167,10 @@ static void colo_compare_init(Object *obj)
(Object **)&s->iothread, (Object **)&s->iothread,
object_property_allow_set_link, object_property_allow_set_link,
OBJ_PROP_LINK_STRONG, NULL); OBJ_PROP_LINK_STRONG, NULL);
/* This parameter just for Xen COLO */
object_property_add_str(obj, "notify_dev",
compare_get_notify_dev, compare_set_notify_dev,
NULL);
s->vnet_hdr = false; s->vnet_hdr = false;
object_property_add_bool(obj, "vnet_hdr_support", compare_get_vnet_hdr, object_property_add_bool(obj, "vnet_hdr_support", compare_get_vnet_hdr,
@ -1071,6 +1185,10 @@ static void colo_compare_finalize(Object *obj)
qemu_chr_fe_deinit(&s->chr_pri_in, false); qemu_chr_fe_deinit(&s->chr_pri_in, false);
qemu_chr_fe_deinit(&s->chr_sec_in, false); qemu_chr_fe_deinit(&s->chr_sec_in, false);
qemu_chr_fe_deinit(&s->chr_out, false); qemu_chr_fe_deinit(&s->chr_out, false);
if (s->notify_dev) {
qemu_chr_fe_deinit(&s->chr_notify_dev, false);
}
if (s->iothread) { if (s->iothread) {
colo_compare_timer_del(s); colo_compare_timer_del(s);
} }
@ -1103,6 +1221,7 @@ static void colo_compare_finalize(Object *obj)
g_free(s->pri_indev); g_free(s->pri_indev);
g_free(s->sec_indev); g_free(s->sec_indev);
g_free(s->outdev); g_free(s->outdev);
g_free(s->notify_dev);
} }
static const TypeInfo colo_compare_info = { static const TypeInfo colo_compare_info = {

View file

@ -64,55 +64,42 @@ static QTAILQ_HEAD(, NetClientState) net_clients;
/***********************************************************/ /***********************************************************/
/* network device redirectors */ /* network device redirectors */
static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
{
const char *p, *p1;
int len;
p = *pp;
p1 = strchr(p, sep);
if (!p1)
return -1;
len = p1 - p;
p1++;
if (buf_size > 0) {
if (len > buf_size - 1)
len = buf_size - 1;
memcpy(buf, p, len);
buf[len] = '\0';
}
*pp = p1;
return 0;
}
int parse_host_port(struct sockaddr_in *saddr, const char *str, int parse_host_port(struct sockaddr_in *saddr, const char *str,
Error **errp) Error **errp)
{ {
char buf[512]; gchar **substrings;
struct hostent *he; struct hostent *he;
const char *p, *r; const char *addr, *p, *r;
int port; int port, ret = 0;
p = str; substrings = g_strsplit(str, ":", 2);
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { if (!substrings || !substrings[0] || !substrings[1]) {
error_setg(errp, "host address '%s' doesn't contain ':' " error_setg(errp, "host address '%s' doesn't contain ':' "
"separating host from port", str); "separating host from port", str);
return -1; ret = -1;
goto out;
} }
addr = substrings[0];
p = substrings[1];
saddr->sin_family = AF_INET; saddr->sin_family = AF_INET;
if (buf[0] == '\0') { if (addr[0] == '\0') {
saddr->sin_addr.s_addr = 0; saddr->sin_addr.s_addr = 0;
} else { } else {
if (qemu_isdigit(buf[0])) { if (qemu_isdigit(addr[0])) {
if (!inet_aton(buf, &saddr->sin_addr)) { if (!inet_aton(addr, &saddr->sin_addr)) {
error_setg(errp, "host address '%s' is not a valid " error_setg(errp, "host address '%s' is not a valid "
"IPv4 address", buf); "IPv4 address", addr);
return -1; ret = -1;
goto out;
} }
} else { } else {
he = gethostbyname(buf); he = gethostbyname(addr);
if (he == NULL) { if (he == NULL) {
error_setg(errp, "can't resolve host address '%s'", buf); error_setg(errp, "can't resolve host address '%s'", addr);
return - 1; ret = -1;
goto out;
} }
saddr->sin_addr = *(struct in_addr *)he->h_addr; saddr->sin_addr = *(struct in_addr *)he->h_addr;
} }
@ -120,10 +107,14 @@ int parse_host_port(struct sockaddr_in *saddr, const char *str,
port = strtol(p, (char **)&r, 0); port = strtol(p, (char **)&r, 0);
if (r == p) { if (r == p) {
error_setg(errp, "port number '%s' is invalid", p); error_setg(errp, "port number '%s' is invalid", p);
return -1; ret = -1;
goto out;
} }
saddr->sin_port = htons(port); saddr->sin_port = htons(port);
return 0;
out:
g_strfreev(substrings);
return ret;
} }
char *qemu_mac_strdup_printf(const uint8_t *macaddr) char *qemu_mac_strdup_printf(const uint8_t *macaddr)
@ -1105,6 +1096,7 @@ static void show_netdevs(void)
static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp) static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
{ {
gchar **substrings = NULL;
void *object = NULL; void *object = NULL;
Error *err = NULL; Error *err = NULL;
int ret = -1; int ret = -1;
@ -1120,28 +1112,33 @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
const char *ip6_net = qemu_opt_get(opts, "ipv6-net"); const char *ip6_net = qemu_opt_get(opts, "ipv6-net");
if (ip6_net) { if (ip6_net) {
char buf[strlen(ip6_net) + 1]; char *prefix_addr;
unsigned long prefix_len = 64; /* Default 64bit prefix length. */
if (get_str_sep(buf, sizeof(buf), &ip6_net, '/') < 0) { substrings = g_strsplit(ip6_net, "/", 2);
/* Default 64bit prefix length. */ if (!substrings || !substrings[0]) {
qemu_opt_set(opts, "ipv6-prefix", ip6_net, &error_abort); error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "ipv6-net",
qemu_opt_set_number(opts, "ipv6-prefixlen", 64, &error_abort); "a valid IPv6 prefix");
} else { goto out;
}
prefix_addr = substrings[0];
if (substrings[1]) {
/* User-specified prefix length. */ /* User-specified prefix length. */
unsigned long len;
int err; int err;
qemu_opt_set(opts, "ipv6-prefix", buf, &error_abort); err = qemu_strtoul(substrings[1], NULL, 10, &prefix_len);
err = qemu_strtoul(ip6_net, NULL, 10, &len);
if (err) { if (err) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"ipv6-prefix", "a number"); "ipv6-prefixlen", "a number");
} else { goto out;
qemu_opt_set_number(opts, "ipv6-prefixlen", len,
&error_abort);
} }
} }
qemu_opt_set(opts, "ipv6-prefix", prefix_addr, &error_abort);
qemu_opt_set_number(opts, "ipv6-prefixlen", prefix_len,
&error_abort);
qemu_opt_unset(opts, "ipv6-net"); qemu_opt_unset(opts, "ipv6-net");
} }
} }
@ -1162,7 +1159,9 @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
qapi_free_NetLegacy(object); qapi_free_NetLegacy(object);
} }
out:
error_propagate(errp, err); error_propagate(errp, err);
g_strfreev(substrings);
visit_free(v); visit_free(v);
return ret; return ret;
} }

View file

@ -1,7 +1,8 @@
# See docs/devel/tracing.txt for syntax documentation. # See docs/devel/tracing.txt for syntax documentation.
# announce.c # announce.c
qemu_announce_self_iter(const char *mac) "%s" qemu_announce_self_iter(const char *id, const char *name, const char *mac, int skip) "%s:%s:%s skip: %d"
qemu_announce_timer_del(bool free_named, bool free_timer, char *id) "free named: %d free timer: %d id: %s"
# vhost-user.c # vhost-user.c
vhost_user_event(const char *chr, int event) "chr: %s got event: %d" vhost_user_event(const char *chr, int event) "chr: %s got event: %d"

View file

@ -699,6 +699,13 @@
# #
# @step: Delay increase (in ms) after each self-announcement attempt # @step: Delay increase (in ms) after each self-announcement attempt
# #
# @interfaces: An optional list of interface names, which restricts the
# announcement to the listed interfaces. (Since 4.1)
#
# @id: A name to be used to identify an instance of announce-timers
# and to allow it to modified later. Not for use as
# part of the migration parameters. (Since 4.1)
#
# Since: 4.0 # Since: 4.0
## ##
@ -706,7 +713,9 @@
'data': { 'initial': 'int', 'data': { 'initial': 'int',
'max': 'int', 'max': 'int',
'rounds': 'int', 'rounds': 'int',
'step': 'int' } } 'step': 'int',
'*interfaces': ['str'],
'*id' : 'str' } }
## ##
# @announce-self: # @announce-self:
@ -718,9 +727,10 @@
# #
# Example: # Example:
# #
# -> { "execute": "announce-self" # -> { "execute": "announce-self",
# "arguments": { # "arguments": {
# "initial": 50, "max": 550, "rounds": 10, "step": 50 } } # "initial": 50, "max": 550, "rounds": 10, "step": 50,
# "interfaces": ["vn2", "vn3"], "id": "bob" } }
# <- { "return": {} } # <- { "return": {} }
# #
# Since: 4.0 # Since: 4.0

View file

@ -10,7 +10,17 @@
* *
* This work is licensed under the terms of the GNU GPL, version 2. See * This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory. * the COPYING file in the top-level directory.
* */
/*
* Known shortcomings:
* - There is no manual page
* - The syntax of the ACL file is not documented anywhere
* - parse_acl_file() doesn't report fopen() failure properly, fails
* to check ferror() after fgets() failure, arbitrarily truncates
* long lines, handles whitespace inconsistently, error messages
* don't point to the offending file and line, errors in included
* files are reported, but otherwise ignored, ...
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"

View file

@ -4477,7 +4477,7 @@ Dump the network traffic on netdev @var{dev} to the file specified by
The file format is libpcap, so it can be analyzed with tools such as tcpdump The file format is libpcap, so it can be analyzed with tools such as tcpdump
or Wireshark. or Wireshark.
@item -object colo-compare,id=@var{id},primary_in=@var{chardevid},secondary_in=@var{chardevid},outdev=@var{chardevid},iothread=@var{id}[,vnet_hdr_support] @item -object colo-compare,id=@var{id},primary_in=@var{chardevid},secondary_in=@var{chardevid},outdev=@var{chardevid},iothread=@var{id}[,vnet_hdr_support][,notify_dev=@var{id}]
Colo-compare gets packet from primary_in@var{chardevid} and secondary_in@var{chardevid}, than compare primary packet with Colo-compare gets packet from primary_in@var{chardevid} and secondary_in@var{chardevid}, than compare primary packet with
secondary packet. If the packets are same, we will output primary secondary packet. If the packets are same, we will output primary
@ -4486,11 +4486,15 @@ do checkpoint and send primary packet to outdev@var{chardevid}.
In order to improve efficiency, we need to put the task of comparison In order to improve efficiency, we need to put the task of comparison
in another thread. If it has the vnet_hdr_support flag, colo compare in another thread. If it has the vnet_hdr_support flag, colo compare
will send/recv packet with vnet_hdr_len. will send/recv packet with vnet_hdr_len.
If you want to use Xen COLO, will need the notify_dev to notify Xen
colo-frame to do checkpoint.
we must use it with the help of filter-mirror and filter-redirector. we must use it with the help of filter-mirror and filter-redirector.
@example @example
KVM COLO
primary: primary:
-netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown -netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown
-device e1000,id=e0,netdev=hn0,mac=52:a4:00:12:78:66 -device e1000,id=e0,netdev=hn0,mac=52:a4:00:12:78:66
@ -4514,6 +4518,33 @@ secondary:
-object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0 -object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0
-object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1 -object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1
Xen COLO
primary:
-netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown
-device e1000,id=e0,netdev=hn0,mac=52:a4:00:12:78:66
-chardev socket,id=mirror0,host=3.3.3.3,port=9003,server,nowait
-chardev socket,id=compare1,host=3.3.3.3,port=9004,server,nowait
-chardev socket,id=compare0,host=3.3.3.3,port=9001,server,nowait
-chardev socket,id=compare0-0,host=3.3.3.3,port=9001
-chardev socket,id=compare_out,host=3.3.3.3,port=9005,server,nowait
-chardev socket,id=compare_out0,host=3.3.3.3,port=9005
-chardev socket,id=notify_way,host=3.3.3.3,port=9009,server,nowait
-object filter-mirror,id=m0,netdev=hn0,queue=tx,outdev=mirror0
-object filter-redirector,netdev=hn0,id=redire0,queue=rx,indev=compare_out
-object filter-redirector,netdev=hn0,id=redire1,queue=rx,outdev=compare0
-object iothread,id=iothread1
-object colo-compare,id=comp0,primary_in=compare0-0,secondary_in=compare1,outdev=compare_out0,notify_dev=nofity_way,iothread=iothread1
secondary:
-netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,down script=/etc/qemu-ifdown
-device e1000,netdev=hn0,mac=52:a4:00:12:78:66
-chardev socket,id=red0,host=3.3.3.3,port=9003
-chardev socket,id=red1,host=3.3.3.3,port=9004
-object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0
-object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1
@end example @end example
If you want to know the detail of above command line, you can read If you want to know the detail of above command line, you can read

View file

@ -184,21 +184,72 @@ static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc)
QDict *rsp; QDict *rsp;
int ret; int ret;
uint16_t *proto = (uint16_t *)&buffer[12]; uint16_t *proto = (uint16_t *)&buffer[12];
size_t total_received = 0;
uint64_t start, now, last_rxt, deadline;
/* Send a set of packets over a few second period */
rsp = qmp("{ 'execute' : 'announce-self', " rsp = qmp("{ 'execute' : 'announce-self', "
" 'arguments': {" " 'arguments': {"
" 'initial': 50, 'max': 550," " 'initial': 20, 'max': 100,"
" 'rounds': 10, 'step': 50 } }"); " 'rounds': 300, 'step': 10, 'id': 'bob' } }");
assert(!qdict_haskey(rsp, "error")); assert(!qdict_haskey(rsp, "error"));
qobject_unref(rsp); qobject_unref(rsp);
/* Catch the packet and make sure it's a RARP */ /* Catch the first packet and make sure it's a RARP */
ret = qemu_recv(sv[0], &len, sizeof(len), 0); ret = qemu_recv(sv[0], &len, sizeof(len), 0);
g_assert_cmpint(ret, ==, sizeof(len)); g_assert_cmpint(ret, ==, sizeof(len));
len = ntohl(len); len = ntohl(len);
ret = qemu_recv(sv[0], buffer, len, 0); ret = qemu_recv(sv[0], buffer, len, 0);
g_assert_cmpint(*proto, ==, htons(ETH_P_RARP)); g_assert_cmpint(*proto, ==, htons(ETH_P_RARP));
/*
* Stop the announcment by settings rounds to 0 on the
* existing timer.
*/
rsp = qmp("{ 'execute' : 'announce-self', "
" 'arguments': {"
" 'initial': 20, 'max': 100,"
" 'rounds': 0, 'step': 10, 'id': 'bob' } }");
assert(!qdict_haskey(rsp, "error"));
qobject_unref(rsp);
/* Now make sure the packets stop */
/* Times are in us */
start = g_get_monotonic_time();
/* 30 packets, max gap 100ms, * 4 for wiggle */
deadline = start + 1000 * (100 * 30 * 4);
last_rxt = start;
while (true) {
int saved_err;
ret = qemu_recv(sv[0], buffer, 60, MSG_DONTWAIT);
saved_err = errno;
now = g_get_monotonic_time();
g_assert_cmpint(now, <, deadline);
if (ret >= 0) {
if (ret) {
last_rxt = now;
}
total_received += ret;
/* Check it's not spewing loads */
g_assert_cmpint(total_received, <, 60 * 30 * 2);
} else {
g_assert_cmpint(saved_err, ==, EAGAIN);
/* 400ms, i.e. 4 worst case gaps */
if ((now - last_rxt) > (1000 * 100 * 4)) {
/* Nothings arrived for a while - must have stopped */
break;
};
/* 100ms */
g_usleep(1000 * 100);
}
};
} }
static void virtio_net_test_cleanup(void *sockets) static void virtio_net_test_cleanup(void *sockets)