-----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:
commit
8ef53cdb50
@ -1944,6 +1944,7 @@ M: Jason Wang <jasowang@redhat.com>
|
||||
S: Maintained
|
||||
F: net/
|
||||
F: include/net/
|
||||
F: qemu-bridge-helper.c
|
||||
T: git https://github.com/jasowang/qemu.git net
|
||||
F: qapi/net.json
|
||||
|
||||
|
@ -955,8 +955,8 @@ ETEXI
|
||||
|
||||
{
|
||||
.name = "announce_self",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.args_type = "interfaces:s?,id:s?",
|
||||
.params = "[interfaces] [id]",
|
||||
.help = "Trigger GARP/RARP announcements",
|
||||
.cmd = hmp_announce_self,
|
||||
},
|
||||
@ -967,6 +967,9 @@ STEXI
|
||||
Trigger a round of GARP/RARP broadcasts; this is useful for explicitly updating the
|
||||
network infrastructure after a reconfiguration or some forms of migration.
|
||||
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
|
||||
|
||||
{
|
||||
|
@ -1017,8 +1017,6 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp)
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
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,
|
||||
object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
|
||||
s);
|
||||
|
@ -2360,7 +2360,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
|
||||
timer_mod(n->announce_timer.tm,
|
||||
qemu_clock_get_ms(n->announce_timer.type));
|
||||
} 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);
|
||||
}
|
||||
|
||||
qemu_announce_timer_del(&n->announce_timer);
|
||||
qemu_announce_timer_del(&n->announce_timer, false);
|
||||
g_free(n->vqs);
|
||||
qemu_del_nic(n->nic);
|
||||
virtio_net_rsc_cleanup(n);
|
||||
|
@ -22,8 +22,12 @@ struct AnnounceTimer {
|
||||
/* Returns: update the timer to the next time point */
|
||||
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
|
||||
|
@ -259,6 +259,8 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
|
||||
void qmp_xen_colo_do_checkpoint(Error **errp)
|
||||
{
|
||||
replication_do_checkpoint_all(errp);
|
||||
/* Notify all filters of all NIC to do checkpoint */
|
||||
colo_notify_filters_event(COLO_EVENT_CHECKPOINT, errp);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "monitor/monitor-internal.h"
|
||||
#include "monitor/qdev.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/opts-visitor.h"
|
||||
#include "qapi/qapi-builtin-visit.h"
|
||||
#include "qapi/qapi-commands-block.h"
|
||||
@ -38,6 +39,7 @@
|
||||
#include "qapi/qapi-commands-run-state.h"
|
||||
#include "qapi/qapi-commands-tpm.h"
|
||||
#include "qapi/qapi-commands-ui.h"
|
||||
#include "qapi/qapi-visit-net.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qerror.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)
|
||||
{
|
||||
NameInfo *info;
|
||||
@ -1631,7 +1659,18 @@ void hmp_info_snapshots(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)
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include "qapi/qapi-commands-net.h"
|
||||
#include "trace.h"
|
||||
|
||||
static GData *named_timers;
|
||||
|
||||
int64_t qemu_announce_timer_step(AnnounceTimer *timer)
|
||||
{
|
||||
int64_t step;
|
||||
@ -31,13 +33,38 @@ int64_t qemu_announce_timer_step(AnnounceTimer *timer)
|
||||
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) {
|
||||
timer_del(timer->tm);
|
||||
timer_free(timer->tm);
|
||||
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
|
||||
* 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);
|
||||
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)
|
||||
{
|
||||
AnnounceTimer *timer = opaque;
|
||||
uint8_t buf[60];
|
||||
int len;
|
||||
bool skip;
|
||||
|
||||
trace_qemu_announce_self_iter(qemu_ether_ntoa(&nic->conf->macaddr));
|
||||
len = announce_self_create(buf, nic->conf->macaddr.a);
|
||||
if (timer->params.has_interfaces) {
|
||||
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 */
|
||||
if (nic->ncs->info->announce) {
|
||||
nic->ncs->info->announce(nic->ncs);
|
||||
trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_",
|
||||
nic->ncs->name,
|
||||
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)
|
||||
{
|
||||
AnnounceTimer *timer = (AnnounceTimer *)opaque;
|
||||
|
||||
qemu_foreach_nic(qemu_announce_self_iter, NULL);
|
||||
qemu_foreach_nic(qemu_announce_self_iter, timer);
|
||||
|
||||
if (--timer->round) {
|
||||
qemu_announce_timer_step(timer);
|
||||
} 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) {
|
||||
qemu_announce_self_once(timer);
|
||||
} else {
|
||||
qemu_announce_timer_del(timer);
|
||||
qemu_announce_timer_del(timer, true);
|
||||
}
|
||||
}
|
||||
|
||||
void qmp_announce_self(AnnounceParameters *params, Error **errp)
|
||||
{
|
||||
static AnnounceTimer announce_timer;
|
||||
qemu_announce_self(&announce_timer, params);
|
||||
AnnounceTimer *named_timer;
|
||||
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);
|
||||
}
|
||||
|
@ -83,11 +83,14 @@ typedef struct CompareState {
|
||||
char *pri_indev;
|
||||
char *sec_indev;
|
||||
char *outdev;
|
||||
char *notify_dev;
|
||||
CharBackend chr_pri_in;
|
||||
CharBackend chr_sec_in;
|
||||
CharBackend chr_out;
|
||||
CharBackend chr_notify_dev;
|
||||
SocketReadState pri_rs;
|
||||
SocketReadState sec_rs;
|
||||
SocketReadState notify_rs;
|
||||
bool vnet_hdr;
|
||||
|
||||
/*
|
||||
@ -117,16 +120,33 @@ enum {
|
||||
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,
|
||||
const uint8_t *buf,
|
||||
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)
|
||||
{
|
||||
@ -238,7 +258,8 @@ static void colo_release_primary_pkt(CompareState *s, Packet *pkt)
|
||||
ret = compare_chr_send(s,
|
||||
pkt->data,
|
||||
pkt->size,
|
||||
pkt->vnet_hdr_len);
|
||||
pkt->vnet_hdr_len,
|
||||
false);
|
||||
if (ret < 0) {
|
||||
error_report("colo send primary packet failed");
|
||||
}
|
||||
@ -430,7 +451,7 @@ sec:
|
||||
qemu_hexdump((char *)spkt->data, stderr,
|
||||
"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,
|
||||
void *user_data)
|
||||
CompareState *s)
|
||||
{
|
||||
GList *result = NULL;
|
||||
int64_t check_time = REGULAR_PACKET_CHECK_MS;
|
||||
@ -583,7 +604,7 @@ static int colo_old_packet_check_one_conn(Connection *conn,
|
||||
|
||||
if (result) {
|
||||
/* Do checkpoint will flush old packet */
|
||||
colo_compare_inconsistency_notify();
|
||||
colo_compare_inconsistency_notify(s);
|
||||
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
|
||||
* 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);
|
||||
}
|
||||
|
||||
@ -632,7 +653,8 @@ static void colo_compare_packet(CompareState *s, Connection *conn,
|
||||
*/
|
||||
trace_colo_compare_main("packet different");
|
||||
g_queue_push_head(&conn->primary_list, pkt);
|
||||
colo_compare_inconsistency_notify();
|
||||
|
||||
colo_compare_inconsistency_notify(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -668,7 +690,8 @@ static void colo_compare_connection(void *opaque, void *user_data)
|
||||
static int compare_chr_send(CompareState *s,
|
||||
const uint8_t *buf,
|
||||
uint32_t size,
|
||||
uint32_t vnet_hdr_len)
|
||||
uint32_t vnet_hdr_len,
|
||||
bool notify_remote_frame)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t len = htonl(size);
|
||||
@ -677,7 +700,14 @@ static int compare_chr_send(CompareState *s,
|
||||
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)) {
|
||||
goto err;
|
||||
}
|
||||
@ -688,13 +718,26 @@ static int compare_chr_send(CompareState *s,
|
||||
* know how to parse net packet correctly.
|
||||
*/
|
||||
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)) {
|
||||
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) {
|
||||
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
|
||||
* 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,
|
||||
compare_sec_chr_in, NULL, NULL,
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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,
|
||||
pri_rs->buf,
|
||||
pri_rs->packet_len,
|
||||
pri_rs->vnet_hdr_len);
|
||||
pri_rs->vnet_hdr_len,
|
||||
false);
|
||||
} else {
|
||||
/* compare packet in the specified connection */
|
||||
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.
|
||||
@ -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->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);
|
||||
|
||||
g_queue_init(&s->conn_list);
|
||||
@ -1024,7 +1133,8 @@ static void colo_flush_packets(void *opaque, void *user_data)
|
||||
compare_chr_send(s,
|
||||
pkt->data,
|
||||
pkt->size,
|
||||
pkt->vnet_hdr_len);
|
||||
pkt->vnet_hdr_len,
|
||||
false);
|
||||
packet_destroy(pkt, NULL);
|
||||
}
|
||||
while (!g_queue_is_empty(&conn->secondary_list)) {
|
||||
@ -1057,6 +1167,10 @@ static void colo_compare_init(Object *obj)
|
||||
(Object **)&s->iothread,
|
||||
object_property_allow_set_link,
|
||||
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;
|
||||
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_sec_in, 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) {
|
||||
colo_compare_timer_del(s);
|
||||
}
|
||||
@ -1103,6 +1221,7 @@ static void colo_compare_finalize(Object *obj)
|
||||
g_free(s->pri_indev);
|
||||
g_free(s->sec_indev);
|
||||
g_free(s->outdev);
|
||||
g_free(s->notify_dev);
|
||||
}
|
||||
|
||||
static const TypeInfo colo_compare_info = {
|
||||
|
99
net/net.c
99
net/net.c
@ -64,55 +64,42 @@ static QTAILQ_HEAD(, NetClientState) net_clients;
|
||||
/***********************************************************/
|
||||
/* 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,
|
||||
Error **errp)
|
||||
{
|
||||
char buf[512];
|
||||
gchar **substrings;
|
||||
struct hostent *he;
|
||||
const char *p, *r;
|
||||
int port;
|
||||
const char *addr, *p, *r;
|
||||
int port, ret = 0;
|
||||
|
||||
p = str;
|
||||
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
||||
substrings = g_strsplit(str, ":", 2);
|
||||
if (!substrings || !substrings[0] || !substrings[1]) {
|
||||
error_setg(errp, "host address '%s' doesn't contain ':' "
|
||||
"separating host from port", str);
|
||||
return -1;
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
addr = substrings[0];
|
||||
p = substrings[1];
|
||||
|
||||
saddr->sin_family = AF_INET;
|
||||
if (buf[0] == '\0') {
|
||||
if (addr[0] == '\0') {
|
||||
saddr->sin_addr.s_addr = 0;
|
||||
} else {
|
||||
if (qemu_isdigit(buf[0])) {
|
||||
if (!inet_aton(buf, &saddr->sin_addr)) {
|
||||
if (qemu_isdigit(addr[0])) {
|
||||
if (!inet_aton(addr, &saddr->sin_addr)) {
|
||||
error_setg(errp, "host address '%s' is not a valid "
|
||||
"IPv4 address", buf);
|
||||
return -1;
|
||||
"IPv4 address", addr);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
he = gethostbyname(buf);
|
||||
he = gethostbyname(addr);
|
||||
if (he == NULL) {
|
||||
error_setg(errp, "can't resolve host address '%s'", buf);
|
||||
return - 1;
|
||||
error_setg(errp, "can't resolve host address '%s'", addr);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
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);
|
||||
if (r == p) {
|
||||
error_setg(errp, "port number '%s' is invalid", p);
|
||||
return -1;
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
saddr->sin_port = htons(port);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
g_strfreev(substrings);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
gchar **substrings = NULL;
|
||||
void *object = NULL;
|
||||
Error *err = NULL;
|
||||
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");
|
||||
|
||||
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) {
|
||||
/* Default 64bit prefix length. */
|
||||
qemu_opt_set(opts, "ipv6-prefix", ip6_net, &error_abort);
|
||||
qemu_opt_set_number(opts, "ipv6-prefixlen", 64, &error_abort);
|
||||
} else {
|
||||
substrings = g_strsplit(ip6_net, "/", 2);
|
||||
if (!substrings || !substrings[0]) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "ipv6-net",
|
||||
"a valid IPv6 prefix");
|
||||
goto out;
|
||||
}
|
||||
|
||||
prefix_addr = substrings[0];
|
||||
|
||||
if (substrings[1]) {
|
||||
/* User-specified prefix length. */
|
||||
unsigned long len;
|
||||
int err;
|
||||
|
||||
qemu_opt_set(opts, "ipv6-prefix", buf, &error_abort);
|
||||
err = qemu_strtoul(ip6_net, NULL, 10, &len);
|
||||
|
||||
err = qemu_strtoul(substrings[1], NULL, 10, &prefix_len);
|
||||
if (err) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
"ipv6-prefix", "a number");
|
||||
} else {
|
||||
qemu_opt_set_number(opts, "ipv6-prefixlen", len,
|
||||
&error_abort);
|
||||
"ipv6-prefixlen", "a number");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
@ -1162,7 +1159,9 @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
|
||||
qapi_free_NetLegacy(object);
|
||||
}
|
||||
|
||||
out:
|
||||
error_propagate(errp, err);
|
||||
g_strfreev(substrings);
|
||||
visit_free(v);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# 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_event(const char *chr, int event) "chr: %s got event: %d"
|
||||
|
@ -699,6 +699,13 @@
|
||||
#
|
||||
# @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
|
||||
##
|
||||
|
||||
@ -706,7 +713,9 @@
|
||||
'data': { 'initial': 'int',
|
||||
'max': 'int',
|
||||
'rounds': 'int',
|
||||
'step': 'int' } }
|
||||
'step': 'int',
|
||||
'*interfaces': ['str'],
|
||||
'*id' : 'str' } }
|
||||
|
||||
##
|
||||
# @announce-self:
|
||||
@ -718,9 +727,10 @@
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# -> { "execute": "announce-self"
|
||||
# -> { "execute": "announce-self",
|
||||
# "arguments": {
|
||||
# "initial": 50, "max": 550, "rounds": 10, "step": 50 } }
|
||||
# "initial": 50, "max": 550, "rounds": 10, "step": 50,
|
||||
# "interfaces": ["vn2", "vn3"], "id": "bob" } }
|
||||
# <- { "return": {} }
|
||||
#
|
||||
# Since: 4.0
|
||||
|
@ -10,7 +10,17 @@
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* 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"
|
||||
|
@ -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
|
||||
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
|
||||
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 another thread. If it has the vnet_hdr_support flag, colo compare
|
||||
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.
|
||||
|
||||
@example
|
||||
|
||||
KVM 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
|
||||
@ -4514,6 +4518,33 @@ secondary:
|
||||
-object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0
|
||||
-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
|
||||
|
||||
If you want to know the detail of above command line, you can read
|
||||
|
@ -184,21 +184,72 @@ static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
QDict *rsp;
|
||||
int ret;
|
||||
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', "
|
||||
" 'arguments': {"
|
||||
" 'initial': 50, 'max': 550,"
|
||||
" 'rounds': 10, 'step': 50 } }");
|
||||
" 'initial': 20, 'max': 100,"
|
||||
" 'rounds': 300, 'step': 10, 'id': 'bob' } }");
|
||||
assert(!qdict_haskey(rsp, "error"));
|
||||
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);
|
||||
g_assert_cmpint(ret, ==, sizeof(len));
|
||||
len = ntohl(len);
|
||||
|
||||
ret = qemu_recv(sv[0], buffer, len, 0);
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user