@@ -69,13 +69,25 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
}
+static struct tracked_binding_datapath *tracked_binding_datapath_create(
+ const struct sbrec_datapath_binding *,
+ bool is_new, struct hmap *tracked_dps);
+static struct tracked_binding_datapath *tracked_binding_datapath_find(
+ struct hmap *, const struct sbrec_datapath_binding *);
+static void tracked_binding_datapath_lport_add(
+ const struct sbrec_port_binding *, struct hmap *tracked_datapaths);
+static void update_lport_tracking(const struct sbrec_port_binding *pb,
+ bool old_claim, bool new_claim,
+ struct hmap *tracked_dp_bindings);
+
static void
add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
struct ovsdb_idl_index *sbrec_port_binding_by_name,
const struct sbrec_datapath_binding *datapath,
bool has_local_l3gateway, int depth,
- struct hmap *local_datapaths)
+ struct hmap *local_datapaths,
+ struct hmap *tracked_datapaths)
{
uint32_t dp_key = datapath->tunnel_key;
struct local_datapath *ld = get_local_datapath(local_datapaths, dp_key);
@@ -92,6 +104,11 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
ld->localnet_port = NULL;
ld->has_local_l3gateway = has_local_l3gateway;
+ if (tracked_datapaths &&
+ !tracked_binding_datapath_find(tracked_datapaths, datapath)) {
+ tracked_binding_datapath_create(datapath, true, tracked_datapaths);
+ }
+
if (depth >= 100) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_WARN_RL(&rl, "datapaths nested too deep");
@@ -124,7 +141,8 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
sbrec_port_binding_by_datapath,
sbrec_port_binding_by_name,
peer->datapath, false,
- depth + 1, local_datapaths);
+ depth + 1, local_datapaths,
+ tracked_datapaths);
}
ld->n_peer_ports++;
if (ld->n_peer_ports > ld->n_allocated_peer_ports) {
@@ -147,12 +165,14 @@ add_local_datapath(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
struct ovsdb_idl_index *sbrec_port_binding_by_name,
const struct sbrec_datapath_binding *datapath,
- bool has_local_l3gateway, struct hmap *local_datapaths)
+ bool has_local_l3gateway, struct hmap *local_datapaths,
+ struct hmap *tracked_datapaths)
{
add_local_datapath__(sbrec_datapath_binding_by_key,
sbrec_port_binding_by_datapath,
sbrec_port_binding_by_name,
- datapath, has_local_l3gateway, 0, local_datapaths);
+ datapath, has_local_l3gateway, 0, local_datapaths,
+ tracked_datapaths);
}
static void
@@ -473,22 +493,45 @@ update_ld_localnet_port(const struct sbrec_port_binding *binding_rec,
static void
update_local_lport_ids(struct sset *local_lport_ids,
- const struct sbrec_port_binding *pb)
+ const struct sbrec_port_binding *pb,
+ struct hmap *tracked_datapaths)
{
char buf[16];
snprintf(buf, sizeof(buf), "%"PRId64"_%"PRId64,
pb->datapath->tunnel_key, pb->tunnel_key);
- sset_add(local_lport_ids, buf);
+ bool added = !!sset_add(local_lport_ids, buf);
+ if (added && tracked_datapaths) {
+ /* Add the 'pb' to the tracked_datapaths. */
+ tracked_binding_datapath_lport_add(pb, tracked_datapaths);
+ }
}
static void
remove_local_lport_ids(const struct sbrec_port_binding *pb,
- struct sset *local_lport_ids)
+ struct sset *local_lport_ids,
+ struct hmap *tracked_datapaths)
{
char buf[16];
snprintf(buf, sizeof(buf), "%"PRId64"_%"PRId64,
pb->datapath->tunnel_key, pb->tunnel_key);
- sset_find_and_delete(local_lport_ids, buf);
+ bool deleted = sset_find_and_delete(local_lport_ids, buf);
+ if (deleted && tracked_datapaths) {
+ /* Add the 'pb' to the tracked_datapaths. */
+ tracked_binding_datapath_lport_add(pb, tracked_datapaths);
+ }
+}
+
+static bool
+add_lport_to_local_lports(struct sset *local_lports, const char *lport_name)
+{
+ return !!sset_add(local_lports, lport_name);
+}
+
+static bool
+remove_lport_from_local_lports(struct sset *local_lports,
+ const char *lport_name)
+{
+ return sset_find_and_delete(local_lports, lport_name);
}
/* Local bindings. binding.c module binds the logical port (represented by
@@ -636,6 +679,75 @@ is_lport_container(const struct sbrec_port_binding *pb)
return is_lport_vif(pb) && pb->parent_port && pb->parent_port[0];
}
+static struct tracked_binding_datapath *
+tracked_binding_datapath_create(const struct sbrec_datapath_binding *dp,
+ bool is_new,
+ struct hmap *tracked_datapaths)
+{
+ struct tracked_binding_datapath *t_dp = xzalloc(sizeof *t_dp);
+ t_dp->dp = dp;
+ t_dp->is_new = is_new;
+ shash_init(&t_dp->lports);
+ hmap_insert(tracked_datapaths, &t_dp->node, uuid_hash(&dp->header_.uuid));
+ return t_dp;
+}
+
+static struct tracked_binding_datapath *
+tracked_binding_datapath_find(struct hmap *tracked_datapaths,
+ const struct sbrec_datapath_binding *dp)
+{
+ struct tracked_binding_datapath *t_dp;
+ size_t hash = uuid_hash(&dp->header_.uuid);
+ HMAP_FOR_EACH_WITH_HASH (t_dp, node, hash, tracked_datapaths) {
+ if (uuid_equals(&t_dp->dp->header_.uuid, &dp->header_.uuid)) {
+ return t_dp;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+tracked_binding_datapath_lport_add(const struct sbrec_port_binding *pb,
+ struct hmap *tracked_datapaths)
+{
+ if (!tracked_datapaths) {
+ return;
+ }
+
+ struct tracked_binding_datapath *tracked_dp =
+ tracked_binding_datapath_find(tracked_datapaths, pb->datapath);
+ if (!tracked_dp) {
+ tracked_dp = tracked_binding_datapath_create(pb->datapath, false,
+ tracked_datapaths);
+ }
+
+ /* Check if the lport is already present or not.
+ * If it is already present, then just update the 'pb' and 'deleted'
+ * fields. */
+ struct tracked_binding_lport *lport =
+ shash_find_data(&tracked_dp->lports, pb->logical_port);
+
+ if (!lport) {
+ lport = xmalloc(sizeof *lport);
+ shash_add(&tracked_dp->lports, pb->logical_port, lport);
+ }
+
+ lport->pb = pb;
+}
+
+void
+binding_tracked_dp_destroy(struct hmap *tracked_datapaths)
+{
+ struct tracked_binding_datapath *t_dp;
+ HMAP_FOR_EACH_POP (t_dp, node, tracked_datapaths) {
+ shash_destroy_free_data(&t_dp->lports);
+ free(t_dp);
+ }
+
+ hmap_destroy(tracked_datapaths);
+}
+
/* Corresponds to each Port_Binding.type. */
enum en_lport_type {
LP_UNKNOWN,
@@ -689,7 +801,7 @@ static bool
claim_lport(const struct sbrec_port_binding *pb,
const struct sbrec_chassis *chassis_rec,
const struct ovsrec_interface *iface_rec,
- bool sb_readonly)
+ bool sb_readonly, struct hmap *tracked_datapaths)
{
if (pb->chassis != chassis_rec) {
if (sb_readonly) {
@@ -708,6 +820,10 @@ claim_lport(const struct sbrec_port_binding *pb,
}
sbrec_port_binding_set_chassis(pb, chassis_rec);
+
+ if (tracked_datapaths) {
+ update_lport_tracking(pb, false, true, tracked_datapaths);
+ }
}
/* Check if the port encap binding, if any, has changed */
@@ -725,9 +841,14 @@ claim_lport(const struct sbrec_port_binding *pb,
/* Returns false if lport is not released due to 'sb_readonly'.
* Returns true otherwise.
+ *
+ * This function assumes that the the 'pb' was claimed
+ * earlier i.e port binding's chassis is set to this chassis.
+ * Caller should make sure that this is the case.
*/
static bool
-release_lport(const struct sbrec_port_binding *pb, bool sb_readonly)
+release_lport(const struct sbrec_port_binding *pb, bool sb_readonly,
+ struct hmap *tracked_datapaths)
{
if (pb->encap) {
if (sb_readonly) {
@@ -750,6 +871,7 @@ release_lport(const struct sbrec_port_binding *pb, bool sb_readonly)
sbrec_port_binding_set_virtual_parent(pb, NULL);
}
+ update_lport_tracking(pb, true, false, tracked_datapaths);
VLOG_INFO("Releasing lport %s from this chassis.", pb->logical_port);
return true;
}
@@ -795,13 +917,14 @@ is_lbinding_container_parent(struct local_binding *lbinding)
static bool
release_local_binding_children(const struct sbrec_chassis *chassis_rec,
struct local_binding *lbinding,
- bool sb_readonly)
+ bool sb_readonly,
+ struct hmap *tracked_dp_bindings)
{
struct shash_node *node;
SHASH_FOR_EACH (node, &lbinding->children) {
struct local_binding *l = node->data;
if (is_lbinding_this_chassis(l, chassis_rec)) {
- if (!release_lport(l->pb, sb_readonly)) {
+ if (!release_lport(l->pb, sb_readonly, tracked_dp_bindings)) {
return false;
}
}
@@ -816,16 +939,17 @@ release_local_binding_children(const struct sbrec_chassis *chassis_rec,
static bool
release_local_binding(const struct sbrec_chassis *chassis_rec,
- struct local_binding *lbinding, bool sb_readonly)
+ struct local_binding *lbinding, bool sb_readonly,
+ struct hmap *tracked_dp_bindings)
{
if (!release_local_binding_children(chassis_rec, lbinding,
- sb_readonly)) {
+ sb_readonly, tracked_dp_bindings)) {
return false;
}
bool retval = true;
if (is_lbinding_this_chassis(lbinding, chassis_rec)) {
- retval = release_lport(lbinding->pb, sb_readonly);
+ retval = release_lport(lbinding->pb, sb_readonly, tracked_dp_bindings);
}
lbinding->pb = NULL;
@@ -846,7 +970,8 @@ consider_vif_lport_(const struct sbrec_port_binding *pb,
if (can_bind) {
/* We can claim the lport. */
if (!claim_lport(pb, b_ctx_in->chassis_rec, lbinding->iface,
- !b_ctx_in->ovnsb_idl_txn)){
+ !b_ctx_in->ovnsb_idl_txn,
+ b_ctx_out->tracked_dp_bindings)){
return false;
}
@@ -854,8 +979,10 @@ consider_vif_lport_(const struct sbrec_port_binding *pb,
b_ctx_in->sbrec_port_binding_by_datapath,
b_ctx_in->sbrec_port_binding_by_name,
pb->datapath, false,
- b_ctx_out->local_datapaths);
- update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
+ b_ctx_out->local_datapaths,
+ b_ctx_out->tracked_dp_bindings);
+ update_local_lport_ids(b_ctx_out->local_lport_ids, pb,
+ b_ctx_out->tracked_dp_bindings);
if (lbinding->iface && qos_map && b_ctx_in->ovs_idl_txn) {
get_qos_params(pb, qos_map);
}
@@ -874,7 +1001,8 @@ consider_vif_lport_(const struct sbrec_port_binding *pb,
if (pb->chassis == b_ctx_in->chassis_rec) {
/* Release the lport if there is no lbinding. */
if (!lbinding_set || !can_bind) {
- return release_lport(pb, !b_ctx_in->ovnsb_idl_txn);
+ return release_lport(pb, !b_ctx_in->ovnsb_idl_txn,
+ b_ctx_out->tracked_dp_bindings);
}
}
@@ -961,7 +1089,8 @@ consider_container_lport(const struct sbrec_port_binding *pb,
* release the container lport, if it was bound earlier. */
if (is_lbinding_this_chassis(container_lbinding,
b_ctx_in->chassis_rec)) {
- return release_lport(pb, !b_ctx_in->ovnsb_idl_txn);
+ return release_lport(pb, !b_ctx_in->ovnsb_idl_txn,
+ b_ctx_out->tracked_dp_bindings);
}
return true;
@@ -1036,18 +1165,22 @@ consider_nonvif_lport_(const struct sbrec_port_binding *pb,
struct binding_ctx_out *b_ctx_out)
{
if (our_chassis) {
- sset_add(b_ctx_out->local_lports, pb->logical_port);
+ add_lport_to_local_lports(b_ctx_out->local_lports, pb->logical_port);
add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
b_ctx_in->sbrec_port_binding_by_datapath,
b_ctx_in->sbrec_port_binding_by_name,
pb->datapath, has_local_l3gateway,
- b_ctx_out->local_datapaths);
+ b_ctx_out->local_datapaths,
+ b_ctx_out->tracked_dp_bindings);
- update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
+ update_local_lport_ids(b_ctx_out->local_lport_ids, pb,
+ b_ctx_out->tracked_dp_bindings);
return claim_lport(pb, b_ctx_in->chassis_rec, NULL,
- !b_ctx_in->ovnsb_idl_txn);
+ !b_ctx_in->ovnsb_idl_txn,
+ b_ctx_out->tracked_dp_bindings);
} else if (pb->chassis == b_ctx_in->chassis_rec) {
- return release_lport(pb, !b_ctx_in->ovnsb_idl_txn);
+ return release_lport(pb, !b_ctx_in->ovnsb_idl_txn,
+ b_ctx_out->tracked_dp_bindings);
}
return true;
@@ -1083,14 +1216,15 @@ consider_localnet_lport(const struct sbrec_port_binding *pb,
struct binding_ctx_out *b_ctx_out,
struct hmap *qos_map)
{
- /* Add all localnet ports to local_lports so that we allocate ct zones
+ /* Add all localnet ports to local_ifaces so that we allocate ct zones
* for them. */
- sset_add(b_ctx_out->local_lports, pb->logical_port);
+ add_lport_to_local_lports(b_ctx_out->local_lports, pb->logical_port);
if (qos_map && b_ctx_in->ovs_idl_txn) {
get_qos_params(pb, qos_map);
}
- update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
+ update_local_lport_ids(b_ctx_out->local_lport_ids, pb,
+ b_ctx_out->tracked_dp_bindings);
}
static bool
@@ -1118,7 +1252,10 @@ consider_ha_lport(const struct sbrec_port_binding *pb,
b_ctx_in->sbrec_port_binding_by_datapath,
b_ctx_in->sbrec_port_binding_by_name,
pb->datapath, false,
- b_ctx_out->local_datapaths);
+ b_ctx_out->local_datapaths,
+ b_ctx_out->tracked_dp_bindings);
+ update_local_lport_ids(b_ctx_out->local_lport_ids, pb,
+ b_ctx_out->tracked_dp_bindings);
}
return consider_nonvif_lport_(pb, our_chassis, false, b_ctx_in, b_ctx_out);
@@ -1184,7 +1321,7 @@ build_local_bindings(struct binding_ctx_in *b_ctx_in,
ovs_assert(lbinding->type == BT_VIF);
}
- sset_add(b_ctx_out->local_lports, iface_id);
+ add_lport_to_local_lports(b_ctx_out->local_lports, iface_id);
smap_replace(b_ctx_out->local_iface_ids, iface_rec->name,
iface_id);
}
@@ -1240,7 +1377,7 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out)
case LP_PATCH:
case LP_LOCALPORT:
case LP_VTEP:
- update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
+ update_local_lport_ids(b_ctx_out->local_lport_ids, pb, NULL);
break;
case LP_VIF:
@@ -1408,7 +1545,8 @@ add_local_datapath_peer_port(const struct sbrec_port_binding *pb,
b_ctx_in->sbrec_port_binding_by_datapath,
b_ctx_in->sbrec_port_binding_by_name,
peer->datapath, false,
- 1, b_ctx_out->local_datapaths);
+ 1, b_ctx_out->local_datapaths,
+ b_ctx_out->tracked_dp_bindings);
return;
}
@@ -1470,7 +1608,8 @@ remove_pb_from_local_datapath(const struct sbrec_port_binding *pb,
struct binding_ctx_out *b_ctx_out,
struct local_datapath *ld)
{
- remove_local_lport_ids(pb, b_ctx_out->local_lport_ids);
+ remove_local_lport_ids(pb, b_ctx_out->local_lport_ids,
+ b_ctx_out->tracked_dp_bindings);
if (!strcmp(pb->type, "patch") ||
!strcmp(pb->type, "l3gateway")) {
remove_local_datapath_peer_port(pb, ld, b_ctx_out->local_datapaths);
@@ -1488,6 +1627,18 @@ remove_pb_from_local_datapath(const struct sbrec_port_binding *pb,
}
}
+static void
+update_lport_tracking(const struct sbrec_port_binding *pb,
+ bool old_claim, bool new_claim,
+ struct hmap *tracked_dp_bindings)
+{
+ if (!tracked_dp_bindings || (old_claim == new_claim)) {
+ return;
+ }
+
+ tracked_binding_datapath_lport_add(pb, tracked_dp_bindings);
+}
+
/* Considers the ovs iface 'iface_rec' for claiming.
* This function should be called if the external_ids:iface-id
* and 'ofport' are set for the 'iface_rec'.
@@ -1500,9 +1651,13 @@ consider_iface_claim(const struct ovsrec_interface *iface_rec,
const char *iface_id,
struct binding_ctx_in *b_ctx_in,
struct binding_ctx_out *b_ctx_out,
- struct hmap *qos_map)
+ struct hmap *qos_map,
+ bool *local_lports_changed)
{
- sset_add(b_ctx_out->local_lports, iface_id);
+ if (add_lport_to_local_lports(b_ctx_out->local_lports, iface_id)) {
+ *local_lports_changed = true;
+ }
+
smap_replace(b_ctx_out->local_iface_ids, iface_rec->name, iface_id);
struct local_binding *lbinding =
@@ -1565,7 +1720,8 @@ static bool
consider_iface_release(const struct ovsrec_interface *iface_rec,
const char *iface_id,
struct binding_ctx_in *b_ctx_in,
- struct binding_ctx_out *b_ctx_out, bool *changed)
+ struct binding_ctx_out *b_ctx_out, bool *changed,
+ bool *local_lports_changed)
{
struct local_binding *lbinding;
lbinding = local_binding_find(b_ctx_out->local_bindings,
@@ -1584,7 +1740,8 @@ consider_iface_release(const struct ovsrec_interface *iface_rec,
* lbinding->iface.
* Cannot access these members of lbinding after this call. */
if (!release_local_binding(b_ctx_in->chassis_rec, lbinding,
- !b_ctx_in->ovnsb_idl_txn)) {
+ !b_ctx_in->ovnsb_idl_txn,
+ b_ctx_out->tracked_dp_bindings)) {
return false;
}
@@ -1597,7 +1754,9 @@ consider_iface_release(const struct ovsrec_interface *iface_rec,
local_binding_delete(b_ctx_out->local_bindings, lbinding);
}
- sset_find_and_delete(b_ctx_out->local_lports, iface_id);
+ if (remove_lport_from_local_lports(b_ctx_out->local_lports, iface_id)) {
+ *local_lports_changed = true;
+ }
smap_remove(b_ctx_out->local_iface_ids, iface_rec->name);
return true;
@@ -1629,6 +1788,8 @@ binding_handle_ovs_interface_changes(struct binding_ctx_in *b_ctx_in,
bool handled = true;
*changed = false;
+ *b_ctx_out->local_lports_changed = false;
+
/* Run the tracked interfaces loop twice. One to handle deleted
* changes. And another to handle add/update changes.
* This will ensure correctness.
@@ -1684,7 +1845,8 @@ binding_handle_ovs_interface_changes(struct binding_ctx_in *b_ctx_in,
if (cleared_iface_id) {
handled = consider_iface_release(iface_rec, cleared_iface_id,
- b_ctx_in, b_ctx_out, changed);
+ b_ctx_in, b_ctx_out, changed,
+ b_ctx_out->local_lports_changed);
}
if (!handled) {
@@ -1726,7 +1888,8 @@ binding_handle_ovs_interface_changes(struct binding_ctx_in *b_ctx_in,
if (iface_id && ofport > 0) {
*changed = true;
handled = consider_iface_claim(iface_rec, iface_id, b_ctx_in,
- b_ctx_out, qos_map_ptr);
+ b_ctx_out, qos_map_ptr,
+ b_ctx_out->local_lports_changed);
if (!handled) {
break;
}
@@ -1791,8 +1954,7 @@ static bool
handle_deleted_vif_lport(const struct sbrec_port_binding *pb,
enum en_lport_type lport_type,
struct binding_ctx_in *b_ctx_in,
- struct binding_ctx_out *b_ctx_out,
- bool *changed)
+ struct binding_ctx_out *b_ctx_out)
{
struct local_binding *lbinding =
get_lbinding_for_lport(pb, lport_type, b_ctx_out);
@@ -1803,13 +1965,12 @@ handle_deleted_vif_lport(const struct sbrec_port_binding *pb,
* clear the 'chassis' column of 'pb'. But we need to do
* for the local_binding's children. */
if (lbinding->type == BT_VIF &&
- !release_local_binding_children(b_ctx_in->chassis_rec,
- lbinding,
- !b_ctx_in->ovnsb_idl_txn)) {
+ !release_local_binding_children(
+ b_ctx_in->chassis_rec, lbinding,
+ !b_ctx_in->ovnsb_idl_txn,
+ b_ctx_out->tracked_dp_bindings)) {
return false;
}
-
- *changed = true;
}
handle_deleted_lport(pb, b_ctx_in, b_ctx_out);
@@ -1821,8 +1982,7 @@ handle_updated_vif_lport(const struct sbrec_port_binding *pb,
enum en_lport_type lport_type,
struct binding_ctx_in *b_ctx_in,
struct binding_ctx_out *b_ctx_out,
- struct hmap *qos_map,
- bool *changed)
+ struct hmap *qos_map)
{
bool claimed = (pb->chassis == b_ctx_in->chassis_rec);
bool handled = true;
@@ -1840,14 +2000,10 @@ handle_updated_vif_lport(const struct sbrec_port_binding *pb,
}
bool now_claimed = (pb->chassis == b_ctx_in->chassis_rec);
- bool changed_ = (claimed != now_claimed);
-
- if (changed_) {
- *changed = true;
- }
+ bool changed = (claimed != now_claimed);
if (lport_type == LP_VIRTUAL ||
- (lport_type == LP_VIF && is_lport_container(pb)) || !changed_) {
+ (lport_type == LP_VIF && is_lport_container(pb)) || !changed) {
return true;
}
@@ -1897,7 +2053,7 @@ binding_handle_port_binding_changes(struct binding_ctx_in *b_ctx_in,
enum en_lport_type lport_type = get_lport_type(pb);
if (lport_type == LP_VIF || lport_type == LP_VIRTUAL) {
handled = handle_deleted_vif_lport(pb, lport_type, b_ctx_in,
- b_ctx_out, changed);
+ b_ctx_out);
} else {
handle_deleted_lport(pb, b_ctx_in, b_ctx_out);
}
@@ -1932,15 +2088,14 @@ binding_handle_port_binding_changes(struct binding_ctx_in *b_ctx_in,
case LP_VIF:
case LP_VIRTUAL:
handled = handle_updated_vif_lport(pb, lport_type, b_ctx_in,
- b_ctx_out, qos_map_ptr,
- changed);
+ b_ctx_out, qos_map_ptr);
break;
case LP_PATCH:
case LP_LOCALPORT:
case LP_VTEP:
- *changed = true;
- update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
+ update_local_lport_ids(b_ctx_out->local_lport_ids, pb,
+ b_ctx_out->tracked_dp_bindings);
if (lport_type == LP_PATCH) {
/* Add the peer datapath to the local datapaths if it's
* not present yet.
@@ -1949,30 +2104,32 @@ binding_handle_port_binding_changes(struct binding_ctx_in *b_ctx_in,
add_local_datapath_peer_port(pb, b_ctx_in, b_ctx_out, ld);
}
}
+
+ if (lport_type == LP_VTEP) {
+ /* VTEP lports are claimed/released by ovn-controller-vteps.
+ * We are not sure what changed. So set *changed to true
+ * for any changes to vtep lports. */
+ *changed = true;
+ }
break;
case LP_L2GATEWAY:
- *changed = true;
handled = consider_l2gw_lport(pb, b_ctx_in, b_ctx_out);
break;
case LP_L3GATEWAY:
- *changed = true;
handled = consider_l3gw_lport(pb, b_ctx_in, b_ctx_out);
break;
case LP_CHASSISREDIRECT:
- *changed = true;
handled = consider_cr_lport(pb, b_ctx_in, b_ctx_out);
break;
case LP_EXTERNAL:
- *changed = true;
handled = consider_external_lport(pb, b_ctx_in, b_ctx_out);
break;
case LP_LOCALNET: {
- *changed = true;
consider_localnet_lport(pb, b_ctx_in, b_ctx_out, qos_map_ptr);
struct shash bridge_mappings =
@@ -2007,6 +2164,10 @@ binding_handle_port_binding_changes(struct binding_ctx_in *b_ctx_in,
}
}
+ if (handled && !hmap_is_empty(b_ctx_out->tracked_dp_bindings)) {
+ *changed = true;
+ }
+
destroy_qos_map(&qos_map);
return handled;
}
@@ -19,6 +19,9 @@
#include <stdbool.h>
#include "openvswitch/shash.h"
+#include "openvswitch/hmap.h"
+#include "openvswitch/uuid.h"
+#include "openvswitch/list.h"
struct hmap;
struct ovsdb_idl;
@@ -54,14 +57,26 @@ struct binding_ctx_in {
struct binding_ctx_out {
struct hmap *local_datapaths;
struct shash *local_bindings;
+ struct sset *local_lports;
+
+ /* If the sset local_lports is modified, this is set to true by
+ * the callee. */
+ bool *local_lports_changed;
+
/* sset of local lport ids in the format
* <datapath-tunnel-key>_<port-tunnel-key>. */
- struct sset *local_lports;
struct sset *local_lport_ids;
+
struct sset *egress_ifaces;
/* smap of OVS interface name as key and
* OVS interface external_ids:iface-id as value. */
struct smap *local_iface_ids;
+
+ /* hmap of 'struct tracked_binding_datapath' which the
+ * callee (binding_handle_ovs_interface_changes and
+ * binding_handle_port_binding_changes) fills in for
+ * the changed datapaths and port bindings. */
+ struct hmap *tracked_dp_bindings;
};
enum local_binding_type {
@@ -86,6 +101,19 @@ local_binding_find(struct shash *local_bindings, const char *name)
return shash_find_data(local_bindings, name);
}
+/* Represents a tracked binding logical port. */
+struct tracked_binding_lport {
+ const struct sbrec_port_binding *pb;
+};
+
+/* Represent a tracked binding datapath. */
+struct tracked_binding_datapath {
+ struct hmap_node node;
+ const struct sbrec_datapath_binding *dp;
+ bool is_new;
+ struct shash lports; /* shash of struct tracked_binding_lport. */
+};
+
void binding_register_ovs_idl(struct ovsdb_idl *);
void binding_run(struct binding_ctx_in *, struct binding_ctx_out *);
bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
@@ -100,4 +128,5 @@ bool binding_handle_ovs_interface_changes(struct binding_ctx_in *,
bool binding_handle_port_binding_changes(struct binding_ctx_in *,
struct binding_ctx_out *,
bool *changed);
+void binding_tracked_dp_destroy(struct hmap *tracked_datapaths);
#endif /* controller/binding.h */
@@ -973,10 +973,88 @@ struct ed_type_runtime_data {
struct sset local_lport_ids;
struct sset active_tunnels;
+ /* runtime data engine private data. */
struct sset egress_ifaces;
struct smap local_iface_ids;
+
+ /* Tracked data. See below for more details and comments. */
+ bool tracked;
+ bool local_lports_changed;
+ struct hmap tracked_dp_bindings;
};
+/* struct ed_type_runtime_data has the below members for tracking the
+ * changes done to the runtime_data engine by the runtime_data engine
+ * handlers. Since this engine is an input to the flow_output engine,
+ * the flow output runtime data handler will make use of this tracked data.
+ *
+ * ------------------------------------------------------------------------
+ * | | This is a hmap of |
+ * | | 'struct tracked_binding_datapath' defined in |
+ * | | binding.h. Runtime data handlers for OVS |
+ * | | Interface and Port Binding changes store the |
+ * | @tracked_dp_bindings | changed datapaths (datapaths added/removed from |
+ * | | local_datapaths) and changed port bindings |
+ * | | (added/updated/deleted in 'local_bindings'). |
+ * | | So any changes to the runtime data - |
+ * | | local_datapaths and local_bindings is captured |
+ * | | here. |
+ * ------------------------------------------------------------------------
+ * | | This is a bool which represents if the runtime |
+ * | | data 'local_lports' changed by the runtime data |
+ * | | handlers for OVS Interface and Port Binding |
+ * | | changes. If 'local_lports' is updated and also |
+ * | | results in any port binding updates, it is |
+ * |@local_lports_changed | captured in the @tracked_dp_bindings. So there |
+ * | | is no need to capture the changes in the |
+ * | | local_lports. If @local_lports_changed is true |
+ * | | but without anydata in the @tracked_dp_bindings,|
+ * | | it means we needto only update the SB monitor |
+ * | | clauses and there isno need for any flow |
+ * | | (re)computations. |
+ * ------------------------------------------------------------------------
+ * | | This represents if the data was tracked or not |
+ * | | by the runtime data handlers during the engine |
+ * | @tracked | run. If the runtime data recompute is |
+ * | | triggered, it means there is no tracked data. |
+ * ------------------------------------------------------------------------
+ *
+ *
+ * The changes to the following runtime_data variables are not tracked.
+ *
+ * ---------------------------------------------------------------------
+ * | local_datapaths | The changes to these runtime data is captured in |
+ * | local_bindings | the @tracked_dp_bindings indirectly and hence it |
+ * | local_lport_ids | is not tracked explicitly. |
+ * ---------------------------------------------------------------------
+ * | local_iface_ids | This is used internally within the runtime data |
+ * | egress_ifaces | engine (used only in binding.c) and hence there |
+ * | | there is no need to track. |
+ * ---------------------------------------------------------------------
+ * | | Active tunnels is built in the |
+ * | | bfd_calculate_active_tunnels() for the tunnel |
+ * | | OVS interfaces. Any changes to non VIF OVS |
+ * | | interfaces results in triggering the full |
+ * | active_tunnels | recompute of runtime data engine and hence there |
+ * | | the tracked data doesn't track it. When we |
+ * | | support handling changes to non VIF OVS |
+ * | | interfaces we need to track the changes to the |
+ * | | active tunnels. |
+ * ---------------------------------------------------------------------
+ *
+ */
+
+static void
+en_runtime_data_clear_tracked_data(void *data_)
+{
+ struct ed_type_runtime_data *data = data_;
+
+ binding_tracked_dp_destroy(&data->tracked_dp_bindings);
+ hmap_init(&data->tracked_dp_bindings);
+ data->local_lports_changed = false;
+ data->tracked = false;
+}
+
static void *
en_runtime_data_init(struct engine_node *node OVS_UNUSED,
struct engine_arg *arg OVS_UNUSED)
@@ -990,6 +1068,10 @@ en_runtime_data_init(struct engine_node *node OVS_UNUSED,
sset_init(&data->egress_ifaces);
smap_init(&data->local_iface_ids);
local_bindings_init(&data->local_bindings);
+
+ /* init the tracked data. */
+ hmap_init(&data->tracked_dp_bindings);
+
return data;
}
@@ -1012,6 +1094,8 @@ en_runtime_data_cleanup(void *data)
}
hmap_destroy(&rt_data->local_datapaths);
local_bindings_destroy(&rt_data->local_bindings);
+
+ en_runtime_data_clear_tracked_data(data);
}
static void
@@ -1092,6 +1176,8 @@ init_binding_ctx(struct engine_node *node,
b_ctx_out->egress_ifaces = &rt_data->egress_ifaces;
b_ctx_out->local_bindings = &rt_data->local_bindings;
b_ctx_out->local_iface_ids = &rt_data->local_iface_ids;
+ b_ctx_out->tracked_dp_bindings = NULL;
+ b_ctx_out->local_lports_changed = NULL;
}
static void
@@ -1103,6 +1189,23 @@ en_runtime_data_run(struct engine_node *node, void *data)
struct sset *local_lport_ids = &rt_data->local_lport_ids;
struct sset *active_tunnels = &rt_data->active_tunnels;
+ /* Clear the (stale) tracked data if any. Even though the tracked data
+ * gets cleared in the beginning of engine_init_run(),
+ * any of the runtime data handler might have set some tracked
+ * data and later another runtime data handler might return false
+ * resulting in full recompute of runtime engine and rendering the tracked
+ * data stale.
+ *
+ * It's possible that engine framework can be enhanced to indicate
+ * the node handlers (in this case flow_output_runtime_data_handler)
+ * that its input node had a full recompute. However we would still
+ * need to clear the tracked data, because we don't want the
+ * stale tracked data to be accessed outside of the engine, since the
+ * tracked data is cleared in the engine_init_run() and not at the
+ * end of the engine run.
+ * */
+ en_runtime_data_clear_tracked_data(data);
+
static bool first_run = true;
if (first_run) {
/* don't cleanup since there is no data yet */
@@ -1158,6 +1261,9 @@ runtime_data_ovs_interface_handler(struct engine_node *node, void *data)
struct binding_ctx_in b_ctx_in;
struct binding_ctx_out b_ctx_out;
init_binding_ctx(node, rt_data, &b_ctx_in, &b_ctx_out);
+ rt_data->tracked = true;
+ b_ctx_out.tracked_dp_bindings = &rt_data->tracked_dp_bindings;
+ b_ctx_out.local_lports_changed = &rt_data->local_lports_changed;
bool changed = false;
if (!binding_handle_ovs_interface_changes(&b_ctx_in, &b_ctx_out,
@@ -1183,6 +1289,9 @@ runtime_data_sb_port_binding_handler(struct engine_node *node, void *data)
return false;
}
+ rt_data->tracked = true;
+ b_ctx_out.tracked_dp_bindings = &rt_data->tracked_dp_bindings;
+
bool changed = false;
if (!binding_handle_port_binding_changes(&b_ctx_in, &b_ctx_out,
&changed)) {
@@ -1790,6 +1899,32 @@ flow_output_port_groups_handler(struct engine_node *node, void *data)
return _flow_output_resource_ref_handler(node, data, REF_TYPE_PORTGROUP);
}
+static bool
+flow_output_runtime_data_handler(struct engine_node *node,
+ void *data OVS_UNUSED)
+{
+ struct ed_type_runtime_data *rt_data =
+ engine_get_input_data("runtime_data", node);
+
+ /* There is no tracked data. Fall back to full recompute of
+ * flow_output. */
+ if (!rt_data->tracked) {
+ return false;
+ }
+
+ if (!hmap_is_empty(&rt_data->tracked_dp_bindings)) {
+ /* We are not yet handling the tracked datapath binding
+ * changes. Return false to trigger full recompute. */
+ return false;
+ }
+
+ if (rt_data->local_lports_changed) {
+ engine_set_node_state(node, EN_UPDATED);
+ }
+
+ return true;
+}
+
/* Engine node en_physical_flow_changes indicates whether
* there is a need to
* - recompute only physical flows or
@@ -1902,13 +2037,6 @@ flow_output_physical_flow_changes_handler(struct engine_node *node, void *data)
return true;
}
-static bool
-flow_output_noop_handler(struct engine_node *node OVS_UNUSED,
- void *data OVS_UNUSED)
-{
- return true;
-}
-
struct ovn_controller_exit_args {
bool *exiting;
bool *restart;
@@ -2030,7 +2158,7 @@ main(int argc, char *argv[])
/* Define inc-proc-engine nodes. */
ENGINE_NODE_CUSTOM_DATA(ct_zones, "ct_zones");
- ENGINE_NODE(runtime_data, "runtime_data");
+ ENGINE_NODE_WITH_CLEAR_TRACK_DATA(runtime_data, "runtime_data");
ENGINE_NODE(mff_ovn_geneve, "mff_ovn_geneve");
ENGINE_NODE(ofctrl_is_connected, "ofctrl_is_connected");
ENGINE_NODE_WITH_CLEAR_TRACK_DATA(physical_flow_changes,
@@ -2067,15 +2195,16 @@ main(int argc, char *argv[])
flow_output_addr_sets_handler);
engine_add_input(&en_flow_output, &en_port_groups,
flow_output_port_groups_handler);
- engine_add_input(&en_flow_output, &en_runtime_data, NULL);
+ engine_add_input(&en_flow_output, &en_runtime_data,
+ flow_output_runtime_data_handler);
engine_add_input(&en_flow_output, &en_mff_ovn_geneve, NULL);
engine_add_input(&en_flow_output, &en_physical_flow_changes,
flow_output_physical_flow_changes_handler);
/* We need this input nodes for only data. Hence the noop handler. */
- engine_add_input(&en_flow_output, &en_ct_zones, flow_output_noop_handler);
+ engine_add_input(&en_flow_output, &en_ct_zones, engine_noop_handler);
engine_add_input(&en_flow_output, &en_ovs_interface,
- flow_output_noop_handler);
+ engine_noop_handler);
engine_add_input(&en_flow_output, &en_ovs_open_vswitch, NULL);
engine_add_input(&en_flow_output, &en_ovs_bridge, NULL);
@@ -286,11 +286,11 @@ for i in 1 2; do
[hv1 hv2], [lflow_run],
[ovn-nbctl --wait=hv lsp-set-type $lsp router]
)
- OVN_CONTROLLER_EXPECT_HIT(
+ OVN_CONTROLLER_EXPECT_NO_HIT(
[hv1 hv2], [lflow_run],
[ovn-nbctl --wait=hv lsp-set-options $lsp router-port=$lrp]
)
- OVN_CONTROLLER_EXPECT_HIT(
+ OVN_CONTROLLER_EXPECT_NO_HIT(
[hv1 hv2], [lflow_run],
[ovn-nbctl --wait=hv lsp-set-addresses $lsp router]
)
@@ -416,15 +416,15 @@ OVN_CONTROLLER_EXPECT_NO_HIT(
[ovn-nbctl --wait=hv destroy Port_Group pg1]
)
-for i in 1 2; do
- ls=ls$i
+OVN_CONTROLLER_EXPECT_HIT(
+ [hv1 hv2], [lflow_run],
+ [ovn-nbctl --wait=hv ls-del ls1]
+)
- # Delete switch $ls
- OVN_CONTROLLER_EXPECT_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv ls-del $ls]
- )
-done
+OVN_CONTROLLER_EXPECT_NO_HIT(
+ [hv1 hv2], [lflow_run],
+ [ovn-nbctl --wait=hv ls-del ls2]
+)
# Delete router lr1
OVN_CONTROLLER_EXPECT_NO_HIT(