[ovs-dev] [PATCH ovn v5 1/9] ovn-controller: Store the local port bindings in the runtime data I-P state.
Dumitru Ceara
dceara at redhat.com
Mon May 11 18:29:53 UTC 2020
On 5/11/20 7:40 PM, Numan Siddique wrote:
>
>
> On Mon, May 11, 2020 at 7:57 PM Dumitru Ceara <dceara at redhat.com
> <mailto:dceara at redhat.com>> wrote:
>
> On 5/11/20 11:45 AM, numans at ovn.org <mailto:numans at ovn.org> wrote:
> > From: Numan Siddique <numans at ovn.org <mailto:numans at ovn.org>>
> >
> > This patch adds a new data structure - 'struct local_binding'
> which represents
> > a probable local port binding. A new object of this structure is
> creared for
> > each interface present in the integration bridge (br-int) with the
> > external_ids:iface-id set. This struct has 2 main fields
> > - 'struct ovsrec_interface *' and 'struct sbrec_port_binding *'.
> These fields
> > are updated during port claim and release.
> >
> > A shash of the local bindings is maintained with the 'iface-id' as
> the hash key
> > in the runtime data of the incremental processing engine.
>
> Hi Numan,
>
>
> Thanks Dumitru for the reviews.
>
>
> What if there's a misconfiguration and two OVS interfaces on the same
> hypervisor have the same iface-id? Isn't it better and more efficient to
> use the interface ofport as key in a hmap instead?
>
>
> I think this is wrong configuration from the userside.
> If this happens, then we should have only one instance of 'struct
> local_binding' and
> ovn-controller can bind either of the port_Binding's. With the present
> master, ovn-controller
> binds only one of port_Binding.
>
> With iface-id as key, when any port binding changes happen we can easily
> look into
> the 'local_bindings' shash using the port_Binding->logical_port.
>
> So I'd stick with using the iface-id as the key.
Ok, makes sense, you're right.
>
> The version v4 of this patch would have handled this use case properly as
> the function - build_local_bindings_from_local_ifaces() was looking for
> 'iface-id'
> in the local_bindings before creating it.This version doesn't do it and
> I'll fix it in v6.
Sounds good.
>
> Thanks.
>
>
>
> More comments inline.
>
> Thanks,
> Dumitru
>
> >
> > This patch helps in the upcoming patch to add incremental
> processing support
> > for OVS interface and SB port binding changes.
> >
> > Signed-off-by: Numan Siddique <numans at ovn.org <mailto:numans at ovn.org>>
> > ---
> > controller/binding.c | 696
> +++++++++++++++++++++---------------
> > controller/binding.h | 16 +-
> > controller/ovn-controller.c | 48 ++-
> > 3 files changed, 439 insertions(+), 321 deletions(-)
> >
> > diff --git a/controller/binding.c b/controller/binding.c
> > index 9d37a23cc..e35440c78 100644
> > --- a/controller/binding.c
> > +++ b/controller/binding.c
> > @@ -69,47 +69,6 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> > ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
> > }
> >
> > -static void
> > -get_local_iface_ids(const struct ovsrec_bridge *br_int,
> > - struct shash *lport_to_iface,
> > - struct sset *local_lports,
> > - struct sset *egress_ifaces)
> > -{
> > - int i;
> > -
> > - for (i = 0; i < br_int->n_ports; i++) {
> > - const struct ovsrec_port *port_rec = br_int->ports[i];
> > - const char *iface_id;
> > - int j;
> > -
> > - if (!strcmp(port_rec->name, br_int->name)) {
> > - continue;
> > - }
> > -
> > - for (j = 0; j < port_rec->n_interfaces; j++) {
> > - const struct ovsrec_interface *iface_rec;
> > -
> > - iface_rec = port_rec->interfaces[j];
> > - iface_id = smap_get(&iface_rec->external_ids,
> "iface-id");
> > - int64_t ofport = iface_rec->n_ofport ?
> *iface_rec->ofport : 0;
> > -
> > - if (iface_id && ofport > 0) {
> > - shash_add(lport_to_iface, iface_id, iface_rec);
> > - sset_add(local_lports, iface_id);
> > - }
> > -
> > - /* Check if this is a tunnel interface. */
> > - if (smap_get(&iface_rec->options, "remote_ip")) {
> > - const char *tunnel_iface
> > - = smap_get(&iface_rec->status,
> "tunnel_egress_iface");
> > - if (tunnel_iface) {
> > - sset_add(egress_ifaces, tunnel_iface);
> > - }
> > - }
> > - }
> > - }
> > -}
> > -
> > static void
> > add_local_datapath__(struct ovsdb_idl_index
> *sbrec_datapath_binding_by_key,
> > struct ovsdb_idl_index
> *sbrec_port_binding_by_datapath,
> > @@ -441,25 +400,11 @@ static bool
> > is_our_chassis(const struct sbrec_chassis *chassis_rec,
> > const struct sbrec_port_binding *binding_rec,
> > const struct sset *active_tunnels,
> > - const struct ovsrec_interface *iface_rec,
> > const struct sset *local_lports)
> > {
> > - /* Ports of type "virtual" should never be explicitly bound
> to an OVS
> > - * port in the integration bridge. If that's the case, ignore
> the binding
> > - * and log a warning.
> > - */
> > - if (iface_rec && !strcmp(binding_rec->type, "virtual")) {
> > - static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(5, 1);
> > - VLOG_WARN_RL(&rl,
> > - "Virtual port %s should not be bound to OVS
> port %s",
> > - binding_rec->logical_port, iface_rec->name);
> > - return false;
> > - }
> > -
> > bool our_chassis = false;
> > - if (iface_rec
> > - || (binding_rec->parent_port &&
> binding_rec->parent_port[0] &&
> > - sset_contains(local_lports, binding_rec->parent_port))) {
> > + if (binding_rec->parent_port && binding_rec->parent_port[0] &&
> > + sset_contains(local_lports, binding_rec->parent_port)) {
> > /* This port is in our chassis unless it is a localport. */
> > our_chassis = strcmp(binding_rec->type, "localport");
> > } else if (!strcmp(binding_rec->type, "l2gateway")) {
> > @@ -481,175 +426,6 @@ is_our_chassis(const struct sbrec_chassis
> *chassis_rec,
> > return our_chassis;
> > }
> >
> > -/* Updates 'binding_rec' and if the port binding is local also
> updates the
> > - * local datapaths and ports.
> > - * Updates and returns the array of local virtual ports that will
> require
> > - * additional processing.
> > - */
> > -static const struct sbrec_port_binding **
> > -consider_local_datapath(const struct sbrec_port_binding *binding_rec,
> > - const struct ovsrec_interface *iface_rec,
> > - struct binding_ctx_in *b_ctx_in,
> > - struct binding_ctx_out *b_ctx_out,
> > - struct hmap *qos_map,
> > - const struct sbrec_port_binding **vpbs,
> > - size_t *n_vpbs, size_t *n_allocated_vpbs)
> > -{
> > - bool our_chassis = is_our_chassis(b_ctx_in->chassis_rec,
> binding_rec,
> > - b_ctx_in->active_tunnels,
> iface_rec,
> > - b_ctx_out->local_lports);
> > - if (iface_rec
> > - || (binding_rec->parent_port &&
> binding_rec->parent_port[0] &&
> > - sset_contains(b_ctx_out->local_lports,
> > - binding_rec->parent_port))) {
> > - if (binding_rec->parent_port &&
> binding_rec->parent_port[0]) {
> > - /* Add child logical port to the set of all local
> ports. */
> > - sset_add(b_ctx_out->local_lports,
> binding_rec->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,
> > - binding_rec->datapath, false,
> > - b_ctx_out->local_datapaths);
> > - if (iface_rec && qos_map && b_ctx_in->ovs_idl_txn) {
> > - get_qos_params(binding_rec, qos_map);
> > - }
> > - } else if (!strcmp(binding_rec->type, "l2gateway")) {
> > - if (our_chassis) {
> > - sset_add(b_ctx_out->local_lports,
> binding_rec->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,
> > - binding_rec->datapath, false,
> > - b_ctx_out->local_datapaths);
> > - }
> > - } else if (!strcmp(binding_rec->type, "chassisredirect")) {
> > - if (ha_chassis_group_contains(binding_rec->ha_chassis_group,
> > - b_ctx_in->chassis_rec)) {
> > -
> 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,
> > - binding_rec->datapath, false,
> > - b_ctx_out->local_datapaths);
> > - }
> > - } else if (!strcmp(binding_rec->type, "l3gateway")) {
> > - if (our_chassis) {
> > -
> 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,
> > - binding_rec->datapath, true,
> > - b_ctx_out->local_datapaths);
> > - }
> > - } else if (!strcmp(binding_rec->type, "localnet")) {
> > - /* Add all localnet ports to local_lports so that we
> allocate ct zones
> > - * for them. */
> > - sset_add(b_ctx_out->local_lports, binding_rec->logical_port);
> > - if (qos_map && b_ctx_in->ovs_idl_txn) {
> > - get_qos_params(binding_rec, qos_map);
> > - }
> > - } else if (!strcmp(binding_rec->type, "external")) {
> > - if (ha_chassis_group_contains(binding_rec->ha_chassis_group,
> > - b_ctx_in->chassis_rec)) {
> > -
> 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,
> > - binding_rec->datapath, false,
> > - b_ctx_out->local_datapaths);
> > - }
> > - }
> > -
> > - if (our_chassis
> > - || !strcmp(binding_rec->type, "patch")
> > - || !strcmp(binding_rec->type, "localport")
> > - || !strcmp(binding_rec->type, "vtep")
> > - || !strcmp(binding_rec->type, "localnet")) {
> > - update_local_lport_ids(b_ctx_out->local_lport_ids,
> binding_rec);
> > - }
> > -
> > - ovs_assert(b_ctx_in->ovnsb_idl_txn);
> > - const char *vif_chassis = smap_get(&binding_rec->options,
> > - "requested-chassis");
> > - bool can_bind = !vif_chassis || !vif_chassis[0]
> > - || !strcmp(vif_chassis,
> b_ctx_in->chassis_rec->name)
> > - || !strcmp(vif_chassis,
> b_ctx_in->chassis_rec->hostname);
> > -
> > - if (can_bind && our_chassis) {
> > - if (binding_rec->chassis != b_ctx_in->chassis_rec) {
> > - if (binding_rec->chassis) {
> > - VLOG_INFO("Changing chassis for lport %s from %s
> to %s.",
> > - binding_rec->logical_port,
> > - binding_rec->chassis->name,
> > - b_ctx_in->chassis_rec->name);
> > - } else {
> > - VLOG_INFO("Claiming lport %s for this chassis.",
> > - binding_rec->logical_port);
> > - }
> > - for (int i = 0; i < binding_rec->n_mac; i++) {
> > - VLOG_INFO("%s: Claiming %s",
> > - binding_rec->logical_port,
> binding_rec->mac[i]);
> > - }
> > - sbrec_port_binding_set_chassis(binding_rec,
> b_ctx_in->chassis_rec);
> > - }
> > - /* Check if the port encap binding, if any, has changed */
> > - struct sbrec_encap *encap_rec =
> > - sbrec_get_port_encap(b_ctx_in->chassis_rec, iface_rec);
> > - if (encap_rec && binding_rec->encap != encap_rec) {
> > - sbrec_port_binding_set_encap(binding_rec, encap_rec);
> > - }
> > - } else if (binding_rec->chassis == b_ctx_in->chassis_rec) {
> > - if (!strcmp(binding_rec->type, "virtual")) {
> > - if (*n_vpbs == *n_allocated_vpbs) {
> > - vpbs = x2nrealloc(vpbs, n_allocated_vpbs, sizeof
> *vpbs);
> > - }
> > - vpbs[(*n_vpbs)] = binding_rec;
> > - (*n_vpbs)++;
> > - } else {
> > - VLOG_INFO("Releasing lport %s from this chassis.",
> > - binding_rec->logical_port);
> > - if (binding_rec->encap) {
> > - sbrec_port_binding_set_encap(binding_rec, NULL);
> > - }
> > - sbrec_port_binding_set_chassis(binding_rec, NULL);
> > - }
> > - } else if (our_chassis) {
> > - static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(5, 1);
> > - VLOG_INFO_RL(&rl,
> > - "Not claiming lport %s, chassis %s "
> > - "requested-chassis %s",
> > - binding_rec->logical_port,
> b_ctx_in->chassis_rec->name,
> > - vif_chassis);
> > - }
> > -
> > - return vpbs;
> > -}
> > -
> > -static void
> > -consider_local_virtual_port(struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> > - const struct sbrec_chassis *chassis_rec,
> > - const struct sbrec_port_binding
> *binding_rec)
> > -{
> > - if (binding_rec->virtual_parent) {
> > - const struct sbrec_port_binding *parent =
> > - lport_lookup_by_name(sbrec_port_binding_by_name,
> > - binding_rec->virtual_parent);
> > - if (parent && parent->chassis == chassis_rec) {
> > - return;
> > - }
> > - }
> > -
> > - /* pinctrl module takes care of binding the ports of type
> 'virtual'.
> > - * Release such ports if their virtual parents are no longer
> claimed by
> > - * this chassis.
> > - */
> > - VLOG_INFO("Releasing lport %s from this chassis.",
> > - binding_rec->logical_port);
> > - if (binding_rec->encap) {
> > - sbrec_port_binding_set_encap(binding_rec, NULL);
> > - }
> > - sbrec_port_binding_set_chassis(binding_rec, NULL);
> > - sbrec_port_binding_set_virtual_parent(binding_rec, NULL);
> > -}
> > -
> > static void
> > add_localnet_egress_interface_mappings(
> > const struct sbrec_port_binding *port_binding,
> > @@ -712,6 +488,345 @@ consider_localnet_port(const struct
> sbrec_port_binding *binding_rec,
> > ld->localnet_port = binding_rec;
> > }
> >
> > +enum local_binding_type {
> > + BT_VIF,
> > + BT_CHILD,
> > + BT_VIRTUAL
> > +};
>
> I think a comment about how local_binding structures are used and their
> hierarchy would be really useful. It might make the code easier to read
> if we describe the different local_binding_type values and how they're
> used here.
>
>
> Ack. I'll address it in v6.
>
>
>
> > +
> > +struct local_binding {
> > + struct ovs_list node; /* In parent if any. */
> > + char *name;
> > + enum local_binding_type type;
> > + const struct ovsrec_interface *iface;
> > + const struct sbrec_port_binding *pb;
> > +
> > + struct ovs_list children;
> > +};
> > +
> > +static struct local_binding *
> > +local_binding_create(const char *name, const struct
> ovsrec_interface *iface,
> > + const struct sbrec_port_binding *pb,
> > + enum local_binding_type type)
> > +{
> > + struct local_binding *lbinding = xzalloc(sizeof *lbinding);
> > + lbinding->name = xstrdup(name);
> > + lbinding->type = type;
> > + lbinding->pb = pb;
> > + lbinding->iface = iface;
> > + ovs_list_init(&lbinding->children);
> > + return lbinding;
> > +}
> > +
> > +static void
> > +local_binding_add(struct shash *local_bindings, struct
> local_binding *lbinding)
> > +{
> > + shash_add(local_bindings, lbinding->name, lbinding);
> > +}
> > +
> > +static struct local_binding *
> > +local_binding_find(struct shash *local_bindings, const char *name)
> > +{
> > + return shash_find_data(local_bindings, name);
> > +}
> > +
> > +void
> > +local_bindings_destroy(struct shash *local_bindings)
> > +{
> > + struct shash_node *node, *next;
> > + SHASH_FOR_EACH_SAFE (node, next, local_bindings) {
> > + struct local_binding *lbinding = node->data;
> > + struct local_binding *c, *n;
> > + LIST_FOR_EACH_SAFE (c, n, node, &lbinding->children) {
> > + ovs_list_remove(&c->node);
> > + free(c->name);
> > + free(c);
> > + }
>
> Here we leak the lbinding->name memory.
>
> > + }
> > +
> > + shash_destroy(local_bindings);
>
> Here we leak the node->data (i.e., struct local_binding) memory. We
> should probably use shash_destroy_free_data().
>
>
> I somehow assumed that shash_destroy will also free the userdata.
>
> I'll fix it in v6.
>
>
>
> > +}
> > +
> > +static void
> > +local_binding_add_child(struct local_binding *lbinding,
> > + struct local_binding *child)
> > +{
> > + struct local_binding *l;
> > + LIST_FOR_EACH (l, node, &lbinding->children) {
> > + if (l == child) {
> > + return;
> > + }
> > + }
>
> Shouldn't we use local_binding_find_child() first before adding a child
> instead? Also, it seems a bit odd that we have to check that the pointer
> is not already in the list.
>
> Actually the caller of this function, first
> calls local_binding_find_child()
> before calling this function. So I'd just remove the for loop.
>
> Another thing is, do we really want to walk lists of children or should
> we use a shash/hmap? It might make sense to stick with list if we don't
> expect a lot of child nodes though.
>
>
> It can happen that a port_Binding can be a parent for
> many other port_bindings in the case of a kubernetes deployed on top
> of openstack using Kuryr. I'll think further and see if we can have a shash.
>
>
Cool, thanks.
>
>
> > +
> > + ovs_list_push_back(&lbinding->children, &child->node);
> > +}
> > +
> > +static struct local_binding *
> > +local_binding_find_child(struct local_binding *lbinding,
> > + const char *child_name)
> > +{
> > + struct local_binding *l;
> > + LIST_FOR_EACH (l, node, &lbinding->children) {
> > + if (!strcmp(l->name, child_name)) {
> > + return l;
> > + }
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +void
> > +binding_add_vport_to_local_bindings(struct shash *local_bindings,
> > + const struct
> sbrec_port_binding *parent,
> > + const struct
> sbrec_port_binding *vport)
>
> This function is never called anywhere as far as I see. And if I
> understand correctly this should be called when incrementally processing
> new port bindings of type "virtual". Does it mean that we just lose the
> update in that case?
>
>
> Actually this is a stale function which is no longer used. It was used
> in earlier versions.
> I forgot to clean this up.
>
> I'll clean it in v6.
>
I see, ok, I need to have another look at the "virtual" binding part
then as I might have missed something.
>
>
> > +{
> > + struct local_binding *lbinding =
> local_binding_find(local_bindings,
> > +
> parent->logical_port);
> > + ovs_assert(lbinding);
> > + struct local_binding *vbinding =
> > + local_binding_find_child(lbinding, vport->logical_port);
> > + if (!vbinding) {
> > + vbinding = local_binding_create(vport->logical_port,
> lbinding->iface,
> > + vport, BT_VIRTUAL);
> > + local_binding_add_child(lbinding, vbinding);
> > + } else {
> > + vbinding->type = BT_VIRTUAL;
> > + }
> > +}
> > +
> > +static void
> > +claim_lport(const struct sbrec_port_binding *pb,
> > + const struct sbrec_chassis *chassis_rec,
> > + const struct ovsrec_interface *iface_rec)
> > +{
> > + if (pb->chassis != chassis_rec) {
> > + if (pb->chassis) {
> > + VLOG_INFO("Changing chassis for lport %s from %s to %s.",
> > + pb->logical_port, pb->chassis->name,
> > + chassis_rec->name);
> > + } else {
> > + VLOG_INFO("Claiming lport %s for this chassis.",
> pb->logical_port);
> > + }
> > + for (int i = 0; i < pb->n_mac; i++) {
> > + VLOG_INFO("%s: Claiming %s", pb->logical_port,
> pb->mac[i]);
> > + }
> > +
> > + sbrec_port_binding_set_chassis(pb, chassis_rec);
> > + }
> > +
> > + /* Check if the port encap binding, if any, has changed */
> > + struct sbrec_encap *encap_rec =
> > + sbrec_get_port_encap(chassis_rec, iface_rec);
> > + if (encap_rec && pb->encap != encap_rec) {
> > + sbrec_port_binding_set_encap(pb, encap_rec);
> > + }
> > +}
> > +
> > +static void
> > +release_lport(const struct sbrec_port_binding *pb)
> > +{
> > + VLOG_INFO("Releasing lport %s from this chassis.",
> pb->logical_port);
> > + if (pb->encap) {
> > + sbrec_port_binding_set_encap(pb, NULL);
> > + }
> > + sbrec_port_binding_set_chassis(pb, NULL);
> > +
> > + if (pb->virtual_parent) {
> > + sbrec_port_binding_set_virtual_parent(pb, NULL);
> > + }
> > +}
> > +
> > +static void
> > +consider_port_binding_for_vif(const struct sbrec_port_binding *pb,
> > + struct binding_ctx_in *b_ctx_in,
> > + enum local_binding_type binding_type,
> > + struct local_binding *lbinding,
> > + struct binding_ctx_out *b_ctx_out,
> > + struct hmap *qos_map)
> > +{
> > + const char *vif_chassis = smap_get(&pb->options,
> "requested-chassis");
> > + bool can_bind = !vif_chassis || !vif_chassis[0]
> > + || !strcmp(vif_chassis,
> b_ctx_in->chassis_rec->name)
> > + || !strcmp(vif_chassis,
> b_ctx_in->chassis_rec->hostname);
> > +
> > + /* Ports of type "virtual" should never be explicitly bound
> to an OVS
> > + * port in the integration bridge. If that's the case, ignore
> the binding
> > + * and log a warning.
> > + */
> > + if (!strcmp(pb->type, "virtual") && lbinding &&
> lbinding->iface &&
> > + binding_type == BT_VIF) {
> > + static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(5, 1);
> > + VLOG_WARN_RL(&rl,
> > + "Virtual port %s should not be bound to OVS
> port %s",
> > + pb->logical_port, lbinding->iface->name);
> > + lbinding->pb = NULL;
> > + return;
> > + }
> > +
> > + if (lbinding && lbinding->pb && can_bind) {
> > + claim_lport(pb, b_ctx_in->chassis_rec, lbinding->iface);
> > +
> > + switch (binding_type) {
> > + case BT_VIF:
> > + lbinding->pb = pb;
> > + break;
> > + case BT_CHILD:
> > + case BT_VIRTUAL: {
> > + /* Add child logical port to the set of all local
> ports. */
> > + sset_add(b_ctx_out->local_lports, pb->logical_port);
> > + struct local_binding *child =
> > + local_binding_find_child(lbinding, pb->logical_port);
> > + if (!child) {
> > + child = local_binding_create(pb->logical_port,
> lbinding->iface,
> > + pb, binding_type);
> > + local_binding_add_child(lbinding, child);
> > + } else {
> > + ovs_assert(child->type == BT_CHILD ||
> > + child->type == BT_VIRTUAL);
> > + child->pb = pb;
> > + child->iface = lbinding->iface;
> > + }
> > + break;
> > + }
> > + default:
> > + OVS_NOT_REACHED();
> > + }
> > +
> > + 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, false,
> b_ctx_out->local_datapaths);
> > + update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
> > + if (lbinding->iface && qos_map && b_ctx_in->ovs_idl_txn) {
> > + get_qos_params(pb, qos_map);
> > + }
> > + } else if (lbinding && lbinding->pb && !can_bind) {
> > + static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(5, 1);
> > + VLOG_INFO_RL(&rl,
> > + "Not claiming lport %s, chassis %s "
> > + "requested-chassis %s",
> > + pb->logical_port,
> > + b_ctx_in->chassis_rec->name,
> > + vif_chassis);
> > + }
> > +
> > + if (pb->chassis == b_ctx_in->chassis_rec) {
> > + if (!lbinding || !lbinding->pb || !can_bind) {
> > + release_lport(pb);
> > + }
> > + }
> > +}
> > +
> > +static void
> > +consider_port_binding(const struct sbrec_port_binding *pb,
> > + struct binding_ctx_in *b_ctx_in,
> > + struct binding_ctx_out *b_ctx_out,
> > + struct hmap *qos_map)
> > +{
> > +
> > + bool our_chassis = is_our_chassis(b_ctx_in->chassis_rec, pb,
> > + b_ctx_in->active_tunnels,
> > + b_ctx_out->local_lports);
> > +
> > + if (!strcmp(pb->type, "l2gateway")) {
> > + if (our_chassis) {
> > + sset_add(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, false,
> > + b_ctx_out->local_datapaths);
> > + }
> > + } else if (!strcmp(pb->type, "chassisredirect")) {
> > + if (ha_chassis_group_contains(pb->ha_chassis_group,
> > + b_ctx_in->chassis_rec)) {
> > +
> 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, false,
> > + b_ctx_out->local_datapaths);
> > + }
> > + } else if (!strcmp(pb->type, "l3gateway")) {
> > + if (our_chassis) {
> > +
> 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, true,
> b_ctx_out->local_datapaths);
> > + }
> > + } else if (!strcmp(pb->type, "localnet")) {
> > + /* Add all localnet ports to local_lports so that we
> allocate ct zones
> > + * for them. */
> > + sset_add(b_ctx_out->local_lports, pb->logical_port);
> > + if (qos_map && b_ctx_in->ovs_idl_txn) {
> > + get_qos_params(pb, qos_map);
> > + }
> > + } else if (!strcmp(pb->type, "external")) {
> > + if (ha_chassis_group_contains(pb->ha_chassis_group,
> > + b_ctx_in->chassis_rec)) {
> > +
> 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, false,
> > + b_ctx_out->local_datapaths);
> > + }
> > + }
> > +
> > + if (our_chassis || !strcmp(pb->type, "localnet")) {
> > + update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
> > + }
> > +
> > + ovs_assert(b_ctx_in->ovnsb_idl_txn);
> > + if (our_chassis) {
> > + claim_lport(pb, b_ctx_in->chassis_rec, NULL);
> > + } else if (pb->chassis == b_ctx_in->chassis_rec) {
> > + release_lport(pb);
> > + }
> > +}
> > +
> > +static void
> > +build_local_bindings_from_local_ifaces(struct binding_ctx_in
> *b_ctx_in,
> > + struct binding_ctx_out
> *b_ctx_out)
> > +{
> > + int i;
> > + for (i = 0; i < b_ctx_in->br_int->n_ports; i++) {
> > + const struct ovsrec_port *port_rec =
> b_ctx_in->br_int->ports[i];
> > + const char *iface_id;
> > + int j;
> > +
> > + if (!strcmp(port_rec->name, b_ctx_in->br_int->name)) {
> > + continue;
> > + }
> > +
> > + for (j = 0; j < port_rec->n_interfaces; j++) {
> > + const struct ovsrec_interface *iface_rec;
> > +
> > + iface_rec = port_rec->interfaces[j];
> > + iface_id = smap_get(&iface_rec->external_ids,
> "iface-id");
> > + int64_t ofport = iface_rec->n_ofport ?
> *iface_rec->ofport : 0;
> > +
> > + if (iface_id && ofport > 0) {
> > + const struct sbrec_port_binding *pb
> > + = lport_lookup_by_name(
> > + b_ctx_in->sbrec_port_binding_by_name,
> iface_id);
> > + struct local_binding *lbinding =
> > + lbinding = local_binding_create(iface_id,
> iface_rec, pb,
> > + BT_VIF);
> > + local_binding_add(b_ctx_out->local_bindings,
> lbinding);
> > + sset_add(b_ctx_out->local_lports, iface_id);
> > + }
> > +
> > + /* Check if this is a tunnel interface. */
> > + if (smap_get(&iface_rec->options, "remote_ip")) {
> > + const char *tunnel_iface
> > + = smap_get(&iface_rec->status,
> "tunnel_egress_iface");
> > + if (tunnel_iface) {
> > + sset_add(b_ctx_out->egress_ifaces, tunnel_iface);
> > + }
> > + }
> > + }
> > + }
> > +}
> > +
> > void
> > binding_run(struct binding_ctx_in *b_ctx_in, struct
> binding_ctx_out *b_ctx_out)
> > {
> > @@ -721,50 +836,62 @@ binding_run(struct binding_ctx_in *b_ctx_in,
> struct binding_ctx_out *b_ctx_out)
> >
> > const struct sbrec_port_binding *binding_rec;
> > struct shash bridge_mappings =
> SHASH_INITIALIZER(&bridge_mappings);
> > - struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
> > - struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
> > struct hmap qos_map;
> >
> > hmap_init(&qos_map);
> > if (b_ctx_in->br_int) {
> > - get_local_iface_ids(b_ctx_in->br_int, &lport_to_iface,
> > - b_ctx_out->local_lports, &egress_ifaces);
> > + build_local_bindings_from_local_ifaces(b_ctx_in, b_ctx_out);
> > }
> >
> > - /* Array to store pointers to local virtual ports. It is
> populated by
> > - * consider_local_datapath.
> > - */
> > - const struct sbrec_port_binding **vpbs = NULL;
> > - size_t n_vpbs = 0;
> > - size_t n_allocated_vpbs = 0;
> > + struct hmap *qos_map_ptr =
> > + !sset_is_empty(b_ctx_out->egress_ifaces) ? &qos_map : NULL;
> >
> > /* Run through each binding record to see if it is resident
> on this
> > * chassis and update the binding accordingly. This includes
> both
> > - * directly connected logical ports and children of those ports.
> > - * Virtual ports are just added to vpbs array and will be
> processed
> > - * later. This is special case for virtual ports is needed in
> order to
> > - * make sure we update the virtual_parent port bindings first.
> > + * directly connected logical ports and children of those ports
> > + * (which also includes virtual ports).
> > */
> > SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec,
> >
> b_ctx_in->port_binding_table) {
> > - const struct ovsrec_interface *iface_rec
> > - = shash_find_data(&lport_to_iface,
> binding_rec->logical_port);
> > - vpbs =
> > - consider_local_datapath(binding_rec,
> > - iface_rec, b_ctx_in, b_ctx_out,
> > - sset_is_empty(&egress_ifaces)
> ? NULL :
> > - &qos_map,
> > - vpbs, &n_vpbs,
> &n_allocated_vpbs);
> > - }
> > -
> > - /* Now also update the virtual ports in case their parent
> ports were
> > - * updated above.
> > - */
> > - for (size_t i = 0; i < n_vpbs; i++) {
> > -
> consider_local_virtual_port(b_ctx_in->sbrec_port_binding_by_name,
> > - b_ctx_in->chassis_rec, vpbs[i]);
> > + if (!strcmp(binding_rec->type, "patch") ||
> > + !strcmp(binding_rec->type, "localport") ||
> > + !strcmp(binding_rec->type, "vtep")) {
> > + update_local_lport_ids(b_ctx_out->local_lport_ids,
> binding_rec);
> > + continue;
> > + }
> > +
> > + bool consider_for_vif = false;
> > + struct local_binding *lbinding = NULL;
> > + enum local_binding_type binding_type = BT_VIF;
> > + if (!binding_rec->type[0]) {
> > + if (!binding_rec->parent_port ||
> !binding_rec->parent_port[0]) {
> > + lbinding =
> local_binding_find(b_ctx_out->local_bindings,
> > +
> binding_rec->logical_port);
> > + } else {
> > + lbinding =
> local_binding_find(b_ctx_out->local_bindings,
> > +
> binding_rec->parent_port);
> > + binding_type = BT_CHILD;
> > + }
> > +
> > + consider_for_vif = true;
> > + } else if (!strcmp(binding_rec->type, "virtual") &&
> > + binding_rec->virtual_parent &&
> > + binding_rec->virtual_parent[0]) {
> > + lbinding = local_binding_find(b_ctx_out->local_bindings,
> > +
> binding_rec->virtual_parent);
> > + consider_for_vif = true;
> > + binding_type = BT_VIRTUAL;
> > + }
> > +
> > + if (consider_for_vif) {
> > + consider_port_binding_for_vif(binding_rec, b_ctx_in,
> > + binding_type, lbinding,
> b_ctx_out,
> > + qos_map_ptr);
> > + } else {
> > + consider_port_binding(binding_rec, b_ctx_in, b_ctx_out,
> > + qos_map_ptr);
> > + }
>
> Is it maybe more readable if we have different port binding handlers
> based on type? I.e., consider_port_binding_<TYPE>:
> - consider_port_binding_vif()
> - consider_port_binding_virtual()
> - consider_port_binding_vport()
> - consider_port_binding_l2gateway()
> - consider_port_binding_chassisredirect()
> - consider_port_binding_l3gateway()
> - consider_port_binding_localnet()
> - consider_port_binding_external()
>
> Right now we have two functions that split the work but do partially
> different things for different types of port bindings.
>
>
> Ok. I'll see if we can have this.
>
>
>
> Also, I think it might be a bit more readable if we'd define some helper
> functions/macros to determine the type of a PB instead of
> combinations of:
> - "!binding_rec->type[0]"
> - "!strcmp(binding_rec->type, ...)"
>
> What do you think?
>
>
> Sounds good. I'll address it in v6.
>
Nice.
>
>
> > }
> > - free(vpbs);
> >
> > add_ovs_bridge_mappings(b_ctx_in->ovs_table,
> b_ctx_in->bridge_table,
> > &bridge_mappings);
> > @@ -776,49 +903,39 @@ binding_run(struct binding_ctx_in *b_ctx_in,
> struct binding_ctx_out *b_ctx_out)
> >
> b_ctx_in->port_binding_table) {
> > if (!strcmp(binding_rec->type, "localnet")) {
> > consider_localnet_port(binding_rec, &bridge_mappings,
> > - &egress_ifaces,
> b_ctx_out->local_datapaths);
> > + b_ctx_out->egress_ifaces,
> > + b_ctx_out->local_datapaths);
> > }
> > }
> > shash_destroy(&bridge_mappings);
> >
> > - if (!sset_is_empty(&egress_ifaces)
> > + if (!sset_is_empty(b_ctx_out->egress_ifaces)
> > && set_noop_qos(b_ctx_in->ovs_idl_txn, b_ctx_in->port_table,
> > - b_ctx_in->qos_table, &egress_ifaces)) {
> > + b_ctx_in->qos_table,
> b_ctx_out->egress_ifaces)) {
> > const char *entry;
> > - SSET_FOR_EACH (entry, &egress_ifaces) {
> > + SSET_FOR_EACH (entry, b_ctx_out->egress_ifaces) {
> > setup_qos(entry, &qos_map);
> > }
> > }
> >
> > - shash_destroy(&lport_to_iface);
> > - sset_destroy(&egress_ifaces);
> > hmap_destroy(&qos_map);
> > }
> >
> > /* Returns true if port-binding changes potentially require flow
> changes on
> > * the current chassis. Returns false if we are sure there is no
> impact. */
> > bool
> > -binding_evaluate_port_binding_changes(
> > - const struct sbrec_port_binding_table *pb_table,
> > - const struct ovsrec_bridge *br_int,
> > - const struct sbrec_chassis *chassis_rec,
> > - struct sset *active_tunnels,
> > - struct sset *local_lports)
> > +binding_evaluate_port_binding_changes(struct binding_ctx_in
> *b_ctx_in,
> > + struct binding_ctx_out
> *b_ctx_out)
> > {
> > - if (!chassis_rec) {
> > + if (!b_ctx_in->chassis_rec) {
> > return true;
> > }
> >
> > bool changed = false;
> >
> > const struct sbrec_port_binding *binding_rec;
> > - struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
> > - struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
> > - if (br_int) {
> > - get_local_iface_ids(br_int, &lport_to_iface, local_lports,
> > - &egress_ifaces);
> > - }
> > - SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (binding_rec,
> pb_table) {
> > + SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (binding_rec,
> > +
> b_ctx_in->port_binding_table) {
> > /* XXX: currently OVSDB change tracking doesn't support
> getting old
> > * data when the operation is update, so if a
> port-binding moved from
> > * this chassis to another, there is no easy way to find
> out the
> > @@ -830,24 +947,31 @@ binding_evaluate_port_binding_changes(
> > * interface table will be updated, which will trigger
> recompute.
> > *
> > * - If the port is not a regular VIF, always trigger
> recompute. */
> > - if (binding_rec->chassis == chassis_rec) {
> > + if (binding_rec->chassis == b_ctx_in->chassis_rec) {
> > changed = true;
> > break;
> > }
> >
> > - const struct ovsrec_interface *iface_rec
> > - = shash_find_data(&lport_to_iface,
> binding_rec->logical_port);
> > - if (is_our_chassis(chassis_rec, binding_rec,
> active_tunnels, iface_rec,
> > - local_lports) ||
> > - (strcmp(binding_rec->type, "") &&
> strcmp(binding_rec->type,
> > - "remote"))) {
> > + if (strcmp(binding_rec->type, "")) {
>
> Unless I'm missing something we lost the '&& strcmp(binding_rec->type,
> "remote")' here. Is that desirable?
>
>
> Yes. It's desirable. For any port binding change of type other than
> normal PB (empty type)
> this function will return true.
>
>
Got it, thanks!
>
>
> > + changed = true;
> > + break;
> > + }
> > +
> > + struct local_binding *lbinding = NULL;
> > + if (!binding_rec->parent_port ||
> !binding_rec->parent_port[0]) {
> > + lbinding = local_binding_find(b_ctx_out->local_bindings,
> > + binding_rec->logical_port);
> > + } else {
> > + lbinding = local_binding_find(b_ctx_out->local_bindings,
> > + binding_rec->parent_port);
> > + }
> > +
> > + if (lbinding) {
> > changed = true;
> > break;
> > }
> > }
> >
> > - shash_destroy(&lport_to_iface);
> > - sset_destroy(&egress_ifaces);
> > return changed;
> > }
> >
> > diff --git a/controller/binding.h b/controller/binding.h
> > index d0b8331af..6bee1d12e 100644
> > --- a/controller/binding.h
> > +++ b/controller/binding.h
> > @@ -31,6 +31,7 @@ struct ovsrec_open_vswitch_table;
> > struct sbrec_chassis;
> > struct sbrec_port_binding_table;
> > struct sset;
> > +struct sbrec_port_binding;
> >
> > struct binding_ctx_in {
> > struct ovsdb_idl_txn *ovnsb_idl_txn;
> > @@ -51,8 +52,10 @@ struct binding_ctx_in {
> >
> > struct binding_ctx_out {
> > struct hmap *local_datapaths;
> > + struct shash *local_bindings;
> > struct sset *local_lports;
> > struct sset *local_lport_ids;
> > + struct sset *egress_ifaces;
> > };
> >
> > void binding_register_ovs_idl(struct ovsdb_idl *);
> > @@ -60,11 +63,10 @@ void binding_run(struct binding_ctx_in *,
> struct binding_ctx_out *);
> > bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> > const struct sbrec_port_binding_table *,
> > const struct sbrec_chassis *);
> > -bool binding_evaluate_port_binding_changes(
> > - const struct sbrec_port_binding_table *,
> > - const struct ovsrec_bridge *br_int,
> > - const struct sbrec_chassis *,
> > - struct sset *active_tunnels,
> > - struct sset *local_lports);
> > -
> > +bool binding_evaluate_port_binding_changes(struct binding_ctx_in *,
> > + struct binding_ctx_out *);
> > +void local_bindings_destroy(struct shash *local_bindings);
> > +void binding_add_vport_to_local_bindings(
> > + struct shash *local_bindings, const struct sbrec_port_binding
> *parent,
> > + const struct sbrec_port_binding *vport);
> > #endif /* controller/binding.h */
> > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> > index c0a97a265..36a1cadb9 100644
> > --- a/controller/ovn-controller.c
> > +++ b/controller/ovn-controller.c
> > @@ -958,6 +958,9 @@ struct ed_type_runtime_data {
> > /* Contains "struct local_datapath" nodes. */
> > struct hmap local_datapaths;
> >
> > + /* Contains "struct local_bindings" nodes. */
>
> s/"struct local_bindings"/"struct local_binding"
>
>
> Ack.
>
>
>
> > + struct shash local_bindings;
> > +
> > /* Contains the name of each logical port resident on the local
> > * hypervisor. These logical ports include the VIFs (and
> their child
> > * logical ports, if any) that belong to VMs running on the
> hypervisor,
> > @@ -969,6 +972,8 @@ struct ed_type_runtime_data {
> > * <datapath-tunnel-key>_<port-tunnel-key> */
> > struct sset local_lport_ids;
> > struct sset active_tunnels;
> > +
> > + struct sset egress_ifaces;
> > };
> >
> > static void *
> > @@ -981,6 +986,8 @@ en_runtime_data_init(struct engine_node *node
> OVS_UNUSED,
> > sset_init(&data->local_lports);
> > sset_init(&data->local_lport_ids);
> > sset_init(&data->active_tunnels);
> > + sset_init(&data->egress_ifaces);
> > + shash_init(&data->local_bindings);
>
> We have a local_bindings_destroy() function that we call below, I think
> it would be nice to have a local_bindings_init() function too. It seems
> a bit weird that the only interface we expose for "struct local_binding"
> is the "destroy" interface.
>
>
> Is it fine even if it is empty ? or you want shash_init to be done in
> that function ?
>
>
I would add the shash_init to the local_bindings_init() function.
Thanks,
Dumitru
>
>
> > return data;
> > }
> >
> > @@ -992,6 +999,7 @@ en_runtime_data_cleanup(void *data)
> > sset_destroy(&rt_data->local_lports);
> > sset_destroy(&rt_data->local_lport_ids);
> > sset_destroy(&rt_data->active_tunnels);
> > + sset_init(&rt_data->egress_ifaces);
> > struct local_datapath *cur_node, *next_node;
> > HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
> > &rt_data->local_datapaths) {
> > @@ -1000,6 +1008,7 @@ en_runtime_data_cleanup(void *data)
> > free(cur_node);
> > }
> > hmap_destroy(&rt_data->local_datapaths);
> > + local_bindings_destroy(&rt_data->local_bindings);
> > }
> >
> > static void
> > @@ -1072,6 +1081,8 @@ init_binding_ctx(struct engine_node *node,
> > b_ctx_out->local_datapaths = &rt_data->local_datapaths;
> > b_ctx_out->local_lports = &rt_data->local_lports;
> > b_ctx_out->local_lport_ids = &rt_data->local_lport_ids;
> > + b_ctx_out->egress_ifaces = &rt_data->egress_ifaces;
> > + b_ctx_out->local_bindings = &rt_data->local_bindings;
> > }
> >
> > static void
> > @@ -1095,12 +1106,16 @@ en_runtime_data_run(struct engine_node
> *node, void *data)
> > free(cur_node);
> > }
> > hmap_clear(local_datapaths);
> > + local_bindings_destroy(&rt_data->local_bindings);
> > sset_destroy(local_lports);
> > sset_destroy(local_lport_ids);
> > sset_destroy(active_tunnels);
> > + sset_destroy(&rt_data->egress_ifaces);
> > sset_init(local_lports);
> > sset_init(local_lport_ids);
> > sset_init(active_tunnels);
> > + sset_init(&rt_data->egress_ifaces);
> > + shash_init(&rt_data->local_bindings);
> > }
> >
> > struct binding_ctx_in b_ctx_in;
> > @@ -1129,35 +1144,12 @@ static bool
> > runtime_data_sb_port_binding_handler(struct engine_node *node,
> void *data)
> > {
> > struct ed_type_runtime_data *rt_data = data;
> > - struct sset *local_lports = &rt_data->local_lports;
> > - struct sset *active_tunnels = &rt_data->active_tunnels;
> > -
> > - struct ovsrec_open_vswitch_table *ovs_table =
> > - (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
> > - engine_get_input("OVS_open_vswitch", node));
> > - struct ovsrec_bridge_table *bridge_table =
> > - (struct ovsrec_bridge_table *)EN_OVSDB_GET(
> > - engine_get_input("OVS_bridge", node));
> > - const char *chassis_id = chassis_get_id();
> > - const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
> ovs_table);
> > -
> > - ovs_assert(br_int && chassis_id);
> > -
> > - struct ovsdb_idl_index *sbrec_chassis_by_name =
> > - engine_ovsdb_node_get_index(
> > - engine_get_input("SB_chassis", node),
> > - "name");
> > -
> > - const struct sbrec_chassis *chassis
> > - = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
> > - ovs_assert(chassis);
> > -
> > - struct sbrec_port_binding_table *pb_table =
> > - (struct sbrec_port_binding_table *)EN_OVSDB_GET(
> > - engine_get_input("SB_port_binding", node));
> > + 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);
> >
> > - bool changed = binding_evaluate_port_binding_changes(
> > - pb_table, br_int, chassis, active_tunnels, local_lports);
> > + bool changed = binding_evaluate_port_binding_changes(&b_ctx_in,
> > + &b_ctx_out);
> >
> > return !changed;
> > }
> >
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org <mailto:dev at openvswitch.org>
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
More information about the dev
mailing list