diff options
Diffstat (limited to 'ovn/controller')
-rw-r--r-- | ovn/controller/chassis.c | 15 | ||||
-rw-r--r-- | ovn/controller/chassis.h | 5 | ||||
-rw-r--r-- | ovn/controller/ofctrl.c | 163 | ||||
-rw-r--r-- | ovn/controller/ofctrl.h | 5 | ||||
-rw-r--r-- | ovn/controller/ovn-controller.c | 19 |
5 files changed, 176 insertions, 31 deletions
diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c index 502e74d50..a1545ecdd 100644 --- a/ovn/controller/chassis.c +++ b/ovn/controller/chassis.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015 Nicira, Inc. +/* Copyright (c) 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,11 +64,13 @@ get_bridge_mappings(const struct smap *ext_ids) return bridge_mappings ? bridge_mappings : ""; } -void +/* Returns this chassis's Chassis record, if it is available and is currently + * amenable to a transaction. */ +const struct sbrec_chassis * chassis_run(struct controller_ctx *ctx, const char *chassis_id) { if (!ctx->ovnsb_idl_txn) { - return; + return NULL; } const struct ovsrec_open_vswitch *cfg; @@ -78,14 +80,14 @@ chassis_run(struct controller_ctx *ctx, const char *chassis_id) cfg = ovsrec_open_vswitch_first(ctx->ovs_idl); if (!cfg) { VLOG_INFO("No Open_vSwitch row defined."); - return; + return NULL; } encap_type = smap_get(&cfg->external_ids, "ovn-encap-type"); encap_ip = smap_get(&cfg->external_ids, "ovn-encap-ip"); if (!encap_type || !encap_ip) { VLOG_INFO("Need to specify an encap type and ip"); - return; + return NULL; } char *tokstr = xstrdup(encap_type); @@ -144,7 +146,7 @@ chassis_run(struct controller_ctx *ctx, const char *chassis_id) if (same) { /* Nothing changed. */ inited = true; - return; + return chassis_rec; } else if (!inited) { struct ds cur_encaps = DS_EMPTY_INITIALIZER; for (int i = 0; i < chassis_rec->n_encaps; i++) { @@ -190,6 +192,7 @@ chassis_run(struct controller_ctx *ctx, const char *chassis_id) free(encaps); inited = true; + return chassis_rec; } /* Returns true if the database is all cleaned up, false if more work is diff --git a/ovn/controller/chassis.h b/ovn/controller/chassis.h index 26017d06c..a14da1cf3 100644 --- a/ovn/controller/chassis.h +++ b/ovn/controller/chassis.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015 Nicira, Inc. +/* Copyright (c) 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,8 @@ struct ovsdb_idl; struct ovsrec_bridge; void chassis_register_ovs_idl(struct ovsdb_idl *); -void chassis_run(struct controller_ctx *, const char *chassis_id); +const struct sbrec_chassis *chassis_run(struct controller_ctx *, + const char *chassis_id); bool chassis_cleanup(struct controller_ctx *, const char *chassis_id); #endif /* ovn/chassis.h */ diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c index f0451b7c1..95f84d18a 100644 --- a/ovn/controller/ofctrl.c +++ b/ovn/controller/ofctrl.c @@ -68,14 +68,9 @@ static char *ovn_flow_to_string(const struct ovn_flow *); static void ovn_flow_log(const struct ovn_flow *, const char *action); static void ovn_flow_destroy(struct ovn_flow *); -static ovs_be32 queue_msg(struct ofpbuf *); -static void queue_flow_mod(struct ofputil_flow_mod *); - /* OpenFlow connection to the switch. */ static struct rconn *swconn; -static void queue_group_mod(struct ofputil_group_mod *); - /* Last seen sequence number for 'swconn'. When this differs from * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */ static unsigned int seqno; @@ -93,6 +88,30 @@ enum ofctrl_state { #undef STATE }; +/* An in-flight update to the switch's flow table. + * + * When we receive a barrier reply from the switch with the given 'xid', we + * know that the switch is caught up to northbound database sequence number + * 'nb_cfg' (and make that available to the client via ofctrl_get_cur_cfg(), so + * that it can store it into our Chassis record's nb_cfg column). */ +struct ofctrl_flow_update { + struct ovs_list list_node; /* In 'flow_updates'. */ + ovs_be32 xid; /* OpenFlow transaction ID for barrier. */ + int64_t nb_cfg; /* Northbound database sequence number. */ +}; + +static struct ofctrl_flow_update * +ofctrl_flow_update_from_list_node(const struct ovs_list *list_node) +{ + return CONTAINER_OF(list_node, struct ofctrl_flow_update, list_node); +} + +/* Currently in-flight updates. */ +static struct ovs_list flow_updates; + +/* nb_cfg of latest committed flow update. */ +static int64_t cur_cfg; + /* Current state. */ static enum ofctrl_state state; @@ -116,10 +135,14 @@ static struct group_table *groups; * S_CLEAR_FLOWS or S_UPDATE_FLOWS, this is really the option we have. */ static enum mf_field_id mff_ovn_geneve; +static ovs_be32 queue_msg(struct ofpbuf *); + static void ovn_flow_table_destroy(void); +static struct ofpbuf *encode_flow_mod(struct ofputil_flow_mod *); static void ovn_group_table_clear(struct group_table *group_table, bool existing); +static struct ofpbuf *encode_group_mod(const struct ofputil_group_mod *); static void ofctrl_recv(const struct ofp_header *, enum ofptype); @@ -132,6 +155,7 @@ ofctrl_init(void) swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION); tx_counter = rconn_packet_counter_create(); hmap_init(&installed_flows); + ovs_list_init(&flow_updates); } /* S_NEW, for a new connection. @@ -330,16 +354,17 @@ run_S_CLEAR_FLOWS(void) .table_id = OFPTT_ALL, .command = OFPFC_DELETE, }; - queue_flow_mod(&fm); + queue_msg(encode_flow_mod(&fm)); VLOG_DBG("clearing all flows"); + /* Send a group_mod to delete all groups. */ struct ofputil_group_mod gm; memset(&gm, 0, sizeof gm); gm.command = OFPGC11_DELETE; gm.group_id = OFPG_ALL; gm.command_bucket_id = OFPG15_BUCKET_ALL; ovs_list_init(&gm.buckets); - queue_group_mod(&gm); + queue_msg(encode_group_mod(&gm)); ofputil_bucket_list_destroy(&gm.buckets); /* Clear installed_flows, to match the state of the switch. */ @@ -350,6 +375,13 @@ run_S_CLEAR_FLOWS(void) ovn_group_table_clear(groups, true); } + /* All flow updates are irrelevant now. */ + struct ofctrl_flow_update *fup, *next; + LIST_FOR_EACH_SAFE (fup, next, list_node, &flow_updates) { + ovs_list_remove(&fup->list_node); + free(fup); + } + state = S_UPDATE_FLOWS; } @@ -378,7 +410,19 @@ run_S_UPDATE_FLOWS(void) static void recv_S_UPDATE_FLOWS(const struct ofp_header *oh, enum ofptype type) { - ofctrl_recv(oh, type); + if (type == OFPTYPE_BARRIER_REPLY && !ovs_list_is_empty(&flow_updates)) { + struct ofctrl_flow_update *fup = ofctrl_flow_update_from_list_node( + ovs_list_front(&flow_updates)); + if (fup->xid == oh->xid) { + if (fup->nb_cfg >= cur_cfg) { + cur_cfg = fup->nb_cfg; + } + ovs_list_remove(&fup->list_node); + free(fup); + } + } else { + ofctrl_recv(oh, type); + } } /* Runs the OpenFlow state machine against 'br_int', which is local to the @@ -476,6 +520,12 @@ ofctrl_destroy(void) ovn_flow_table_destroy(); rconn_packet_counter_destroy(tx_counter); } + +int64_t +ofctrl_get_cur_cfg(void) +{ + return cur_cfg; +} static ovs_be32 queue_msg(struct ofpbuf *msg) @@ -765,15 +815,21 @@ ovn_flow_table_destroy(void) /* Flow table update. */ -static void -queue_flow_mod(struct ofputil_flow_mod *fm) +static struct ofpbuf * +encode_flow_mod(struct ofputil_flow_mod *fm) { fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_ANY; fm->out_group = OFPG_ANY; - queue_msg(ofputil_encode_flow_mod(fm, OFPUTIL_P_OF13_OXM)); + return ofputil_encode_flow_mod(fm, OFPUTIL_P_OF13_OXM); } +static void +add_flow_mod(struct ofputil_flow_mod *fm, struct ovs_list *msgs) +{ + struct ofpbuf *msg = encode_flow_mod(fm); + ovs_list_push_back(msgs, &msg->list_node); +} /* group_table. */ @@ -811,10 +867,17 @@ ovn_group_table_clear(struct group_table *group_table, bool existing) } } +static struct ofpbuf * +encode_group_mod(const struct ofputil_group_mod *gm) +{ + return ofputil_encode_group_mod(OFP13_VERSION, gm); +} + static void -queue_group_mod(struct ofputil_group_mod *gm) +add_group_mod(const struct ofputil_group_mod *gm, struct ovs_list *msgs) { - queue_msg(ofputil_encode_group_mod(OFP13_VERSION, gm)); + struct ofpbuf *msg = encode_group_mod(gm); + ovs_list_push_back(msgs, &msg->list_node); } @@ -829,7 +892,7 @@ queue_group_mod(struct ofputil_group_mod *gm) * * This should be called after ofctrl_run() within the main loop. */ void -ofctrl_put(struct group_table *group_table) +ofctrl_put(struct group_table *group_table, int64_t nb_cfg) { if (!groups) { groups = group_table; @@ -845,6 +908,9 @@ ofctrl_put(struct group_table *group_table) return; } + /* OpenFlow messages to send to the switch to bring it up-to-date. */ + struct ovs_list msgs = OVS_LIST_INITIALIZER(&msgs); + /* Iterate through all the desired groups. If there are new ones, * add them to the switch. */ struct group_info *desired; @@ -862,7 +928,7 @@ ofctrl_put(struct group_table *group_table) ds_cstr(&group_string), &usable_protocols); if (!error) { - queue_group_mod(&gm); + add_group_mod(&gm, &msgs); } else { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_ERR_RL(&rl, "new group %s %s", error, @@ -890,7 +956,7 @@ ofctrl_put(struct group_table *group_table) .table_id = i->table_id, .command = OFPFC_DELETE_STRICT, }; - queue_flow_mod(&fm); + add_flow_mod(&fm, &msgs); ovn_flow_log(i, "removing installed"); hmap_remove(&installed_flows, &i->match_hmap_node); @@ -917,7 +983,7 @@ ofctrl_put(struct group_table *group_table) .ofpacts_len = d->ofpacts_len, .command = OFPFC_MODIFY_STRICT, }; - queue_flow_mod(&fm); + add_flow_mod(&fm, &msgs); ovn_flow_log(i, "updating installed"); /* Replace 'i''s actions by 'd''s. */ @@ -950,7 +1016,7 @@ ofctrl_put(struct group_table *group_table) .ofpacts_len = d->ofpacts_len, .command = OFPFC_ADD, }; - queue_flow_mod(&fm); + add_flow_mod(&fm, &msgs); ovn_flow_log(d, "adding installed"); /* Copy 'd' from 'flow_table' to installed_flows. */ @@ -977,7 +1043,7 @@ ofctrl_put(struct group_table *group_table) ds_cstr(&group_string), &usable_protocols); if (!error) { - queue_group_mod(&gm); + add_group_mod(&gm, &msgs); } else { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_ERR_RL(&rl, "Error deleting group %d: %s", @@ -1009,4 +1075,63 @@ ofctrl_put(struct group_table *group_table) free(desired); } } + + if (!ovs_list_is_empty(&msgs)) { + /* Add a barrier to the list of messages. */ + struct ofpbuf *barrier = ofputil_encode_barrier_request(OFP13_VERSION); + const struct ofp_header *oh = barrier->data; + ovs_be32 xid = oh->xid; + ovs_list_push_back(&msgs, &barrier->list_node); + + /* Queue the messages. */ + struct ofpbuf *msg; + LIST_FOR_EACH_POP (msg, list_node, &msgs) { + queue_msg(msg); + } + + /* Track the flow update. */ + struct ofctrl_flow_update *fup, *prev; + LIST_FOR_EACH_REVERSE_SAFE (fup, prev, list_node, &flow_updates) { + if (nb_cfg < fup->nb_cfg) { + /* This ofctrl_flow_update is for a configuration later than + * 'nb_cfg'. This should not normally happen, because it means + * that 'nb_cfg' in the SB_Global table of the southbound + * database decreased, and it should normally be monotonically + * increasing. */ + VLOG_WARN("nb_cfg regressed from %"PRId64" to %"PRId64, + fup->nb_cfg, nb_cfg); + ovs_list_remove(&fup->list_node); + free(fup); + } else if (nb_cfg == fup->nb_cfg) { + /* This ofctrl_flow_update is for the same configuration as + * 'nb_cfg'. Probably, some change to the physical topology + * means that we had to revise the OpenFlow flow table even + * though the logical topology did not change. Update fp->xid, + * so that we don't send a notification that we're up-to-date + * until we're really caught up. */ + VLOG_DBG("advanced xid target for nb_cfg=%"PRId64, nb_cfg); + fup->xid = xid; + goto done; + } else { + break; + } + } + + /* Add a flow update. */ + fup = xmalloc(sizeof *fup); + ovs_list_push_back(&flow_updates, &fup->list_node); + fup->xid = xid; + fup->nb_cfg = nb_cfg; + done:; + } else if (!ovs_list_is_empty(&flow_updates)) { + /* Getting up-to-date with 'nb_cfg' didn't require any extra flow table + * changes, so whenever we get up-to-date with the most recent flow + * table update, we're also up-to-date with 'nb_cfg'. */ + struct ofctrl_flow_update *fup = ofctrl_flow_update_from_list_node( + ovs_list_back(&flow_updates)); + fup->nb_cfg = nb_cfg; + } else { + /* We were completely up-to-date before and still are. */ + cur_cfg = nb_cfg; + } } diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h index 49b95b0fd..befae0175 100644 --- a/ovn/controller/ofctrl.h +++ b/ovn/controller/ofctrl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015 Nicira, Inc. +/* Copyright (c) 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,9 +32,10 @@ struct group_table; /* Interface for OVN main loop. */ void ofctrl_init(void); enum mf_field_id ofctrl_run(const struct ovsrec_bridge *br_int); -void ofctrl_put(struct group_table *group_table); +void ofctrl_put(struct group_table *group_table, int64_t nb_cfg); void ofctrl_wait(void); void ofctrl_destroy(void); +int64_t ofctrl_get_cur_cfg(void); struct ovn_flow *ofctrl_dup_flow(struct ovn_flow *source); diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c index 4d9490a5a..ecf13063f 100644 --- a/ovn/controller/ovn-controller.c +++ b/ovn/controller/ovn-controller.c @@ -299,6 +299,13 @@ update_ct_zones(struct sset *lports, struct hmap *patched_datapaths, sset_destroy(&all_users); } +static int64_t +get_nb_cfg(struct ovsdb_idl *idl) +{ + const struct sbrec_sb_global *sb = sbrec_sb_global_first(idl); + return sb ? sb->nb_cfg : 0; +} + /* Contains "struct local_datapath" nodes whose hash values are the * tunnel_key of datapaths with at least one local port binding. */ static struct hmap local_datapaths = HMAP_INITIALIZER(&local_datapaths); @@ -378,6 +385,7 @@ main(int argc, char *argv[]) char *ovnsb_remote = get_ovnsb_remote(ovs_idl_loop.idl); struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true)); + ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg); /* Track the southbound idl. */ ovsdb_idl_track_add_all(ovnsb_idl_loop.idl); @@ -422,8 +430,9 @@ main(int argc, char *argv[]) const struct ovsrec_bridge *br_int = get_br_int(&ctx); const char *chassis_id = get_chassis_id(ctx.ovs_idl); + const struct sbrec_chassis *chassis = NULL; if (chassis_id) { - chassis_run(&ctx, chassis_id); + chassis = chassis_run(&ctx, chassis_id); encaps_run(&ctx, br_int, chassis_id); binding_run(&ctx, br_int, chassis_id, &local_datapaths); } @@ -448,7 +457,13 @@ main(int argc, char *argv[]) br_int, chassis_id, &ct_zones, &local_datapaths, &patched_datapaths); - ofctrl_put(&group_table); + ofctrl_put(&group_table, get_nb_cfg(ctx.ovnsb_idl)); + if (ctx.ovnsb_idl_txn) { + int64_t cur_cfg = ofctrl_get_cur_cfg(); + if (cur_cfg && cur_cfg != chassis->nb_cfg) { + sbrec_chassis_set_nb_cfg(chassis, cur_cfg); + } + } } sset_destroy(&all_lports); |