On Mon, Jun 13, 2022 at 3:17 PM Han Zhou <hz...@ovn.org> wrote: > > > > On Mon, Jun 13, 2022 at 11:40 AM Ihar Hrachyshka <ihrac...@redhat.com> wrote: > > > > On Thu, Jun 9, 2022 at 3:01 AM Han Zhou <hz...@ovn.org> wrote: > > > > > > > > > > > > On Tue, Jun 7, 2022 at 7:06 PM Ihar Hrachyshka <ihrac...@redhat.com> > > > wrote: > > > > > > > > When options:activation-strategy is set to "rarp" for LSP, when used in > > > > combination with multiple chassis names listed in > > > > options:requested-chassis, additional chassis will install special flows > > > > that would block all ingress and egress traffic for the port until a > > > > special activation event happens. > > > > > > > > For "rarp" strategy, an observation of a RARP packet sent from the port > > > > on the additional chassis is such an event. When it occurs, a special > > > > flow passes control to a controller() action handler that eventually > > > > removes the installed blocking flows and also marks the port as > > > > options:additional-chassis-activated in southbound db. > > > > > > > > This feature is useful in live migration scenarios where it's not > > > > advisable to unlock the destination port location prematurily to avoid > > > > duplicate packets originating from the port. > > > > > > > > Signed-off-by: Ihar Hrachyshka <ihrac...@redhat.com> > > > > > > Thanks for the revision. Please see my comments below: > > > > > > > + > > > > +static void > > > > +en_activated_ports_run(struct engine_node *node, void *data_) > > > > +{ > > > > + struct ed_type_activated_ports *data = data_; > > > > + enum engine_node_state state = EN_UNCHANGED; > > > > + > > > > + if (!data->activated_ports) { > > > > + get_activated_ports(&data->activated_ports); > > > > + if (data->activated_ports) { > > > > + state = EN_UPDATED; > > > > + } > > > > + } else { > > > > > > This is not possible because en_activated_ports_clear_tracked_data() will > > > always be called before engine_run(). So this branch is not really needed. > > > > > > > + struct ovs_list *new_activated_ports; > > > > + bool ap_changed = get_activated_ports(&new_activated_ports); > > > > + if (ap_changed && new_activated_ports) { > > > > + en_activated_ports_cleanup(data); > > > > + data->activated_ports = new_activated_ports; > > > > + state = EN_UPDATED; > > > > + } > > > > + } > > > > + engine_set_node_state(node, state); > > > > +} > > > > + > > > > +static bool > > > > +runtime_data_activated_ports_handler(struct engine_node *node, void > > > > *data) > > > > > > nit: better to put this function together with runtime_data_xxx > > > functions, since this is how we organize the functions for each engine > > > node. > > > > > > > Hm. Is it not? Right after this function, > > runtime_data_ovs_interface_shadow_handler goes. > > > That's because the new en_activated_ports related data structure and > functions you added in the middle of the en_runtime_data functions :) > But I think there is no need for another revision just for this. Anyone who > applies the change can move the block of code to a proper place. >
Ouch, sorry. :( I'll move if I need another revision. > > > ... > > > > +static void > > > > +setup_rarp_activation_strategy(const struct sbrec_port_binding > > > > *binding, > > > > + ofp_port_t ofport, struct zone_ids > > > > *zone_ids, > > > > + struct ovn_desired_flow_table > > > > *flow_table, > > > > + struct ofpbuf *ofpacts_p) > > > > +{ > > > > + struct match match = MATCH_CATCHALL_INITIALIZER; > > > > + > > > > + /* Unblock the port on ingress RARP. */ > > > > + match_set_dl_type(&match, htons(ETH_TYPE_RARP)); > > > > + match_set_in_port(&match, ofport); > > > > + ofpbuf_clear(ofpacts_p); > > > > + > > > > + load_logical_ingress_metadata(binding, zone_ids, ofpacts_p); > > > > + > > > > + size_t ofs = ofpacts_p->size; > > > > + struct ofpact_controller *oc = ofpact_put_CONTROLLER(ofpacts_p); > > > > + oc->max_len = UINT16_MAX; > > > > + oc->reason = OFPR_ACTION; > > > > + > > > > + struct action_header ah = { > > > > + .opcode = htonl(ACTION_OPCODE_ACTIVATION_STRATEGY_RARP) > > > > + }; > > > > + ofpbuf_put(ofpacts_p, &ah, sizeof ah); > > > > + > > > > + ofpacts_p->header = oc; > > > > + oc->userdata_len = ofpacts_p->size - (ofs + sizeof *oc); > > > > + ofpact_finish_CONTROLLER(ofpacts_p, &oc); > > > > + put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p); > > > > + > > > > + ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 1010, > > > > + binding->header_.uuid.parts[0], > > > > + &match, ofpacts_p, &binding->header_.uuid); > > > > + ofpbuf_clear(ofpacts_p); > > > > + > > > > + /* Block all non-RARP traffic for the port, both directions. */ > > > > + match_init_catchall(&match); > > > > + match_set_in_port(&match, ofport); > > > > + > > > > + ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 1000, > > > > + binding->header_.uuid.parts[0], > > > > + &match, ofpacts_p, &binding->header_.uuid); > > > > + > > > > + match_init_catchall(&match); > > > > + uint32_t dp_key = binding->datapath->tunnel_key; > > > > + uint32_t port_key = binding->tunnel_key; > > > > + match_set_metadata(&match, htonll(dp_key)); > > > > + match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); > > > > + > > > > + ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 1000, > > > > + binding->header_.uuid.parts[0], > > > > + &match, ofpacts_p, &binding->header_.uuid); > > > > > > Recall my comment for v6: > > > >> What if another LSP on the same chassis (an additional chassis of the > > > >> migrating LSP) wants to send a packet to the migrating LSP on the main > > > >> chassis? Would it be blocked, too? > > > > > > > Packets are cloned to all chassis, delivered to all of them, and each > > > of them decides on its own if it's ok to deliver it (depending on > > > whether activation flows are still present). I'll update the test case > > > to cover the scenario you describe. > > > > > > What I meant was that: the flow is added to a datapath of LS, with the > > > outport matching the LSP that is being migrated, consider the example > > > below: > > > LSP1 is migrating from HV1 to HV2. Now LSP2 on the same datapath and > > > bound on HV2 is sending a packet destined to LSP1, it is expected that > > > the packet is delivered to HV1 at this moment, right? However, because of > > > the above flow, it would match (both dp_key and port_key), so the packet > > > will be dropped. Did I misunderstand? > > > > > > > There are two drop flows set up on HV2 related to activation strategy: > > > > cookie=0xc2723b3d, duration=2.150s, table=0, n_packets=0, n_bytes=0, > > idle_age=2, priority=1000,in_port=3 actions=drop > > cookie=0xc2723b3d, duration=2.151s, table=65, n_packets=3, > > n_bytes=126, idle_age=0, priority=1000,reg15=0x1,metadata=0x1 > > actions=drop > > > > The first flow is for traffic that originate from the unactivated > > port. The second flow is for traffic that is directed to the port. > > Your scenario involves the second flow. In this case, first > > table=37-39 flows are triggered that will clone packets to HV1, among > > other things; then once we reach table=65, delivery to HV2 endpoint > > will be blocked. > > > > I believe this scenario is already covered in > > "options:activation-strategy for logical port", specifically see the > > following check: > > > > # Packet from hv2:Second arrives to hv1:Migrator > > # hv2:Second cannot reach hv2:Migrator because it is blocked by RARP > > strategy > > request=$(send_arp hv2 second 000000000002 000000000010 $second_spa > > $migrator_spa) > > echo $request >> hv1/migrator.expected > > > > You are right. Thanks for explaining and sorry that I was misled by something > wrong in my memory. > > > > ... > > > > > > > > +static bool activated_ports_changed = false; > > > > +static struct ovs_list activated_ports = OVS_LIST_INITIALIZER( > > > > + &activated_ports); > > > > + > > > > +bool > > > > +get_activated_ports(struct ovs_list **ap) > > > > +{ > > > > + ovs_mutex_lock(&pinctrl_mutex); > > > > + if (ovs_list_is_empty(&activated_ports)) { > > > > + activated_ports_changed = false; > > > > + ovs_mutex_unlock(&pinctrl_mutex); > > > > + *ap = NULL; > > > > + return false; > > > > + } > > > > + > > > > + struct activated_port *pp; > > > > + *ap = xmalloc(sizeof **ap); > > > > + ovs_list_init(*ap); > > > > + > > > > + LIST_FOR_EACH (pp, list, &activated_ports) { > > > > + struct activated_port *pp_copy = xmalloc(sizeof *pp_copy); > > > > + pp_copy->port_key = pp->port_key; > > > > + pp_copy->dp_key = pp->dp_key; > > > > + ovs_list_push_front(*ap, &pp_copy->list); > > > > + } > > > > + bool ap_changed = activated_ports_changed; > > > > > > Instead of a global flag, I guess we will need a per entry flag in the > > > activated_ports list, to mark the entries as "consumed by I-P engine" > > > once it is returned by this function. And this function should only > > > return the entries that have not yet returned before. (alternatively, > > > just use too different lists). > > > Otherwise, the list may contain entries that are consumed by I-P engine > > > but not yet made it to the SB DB, and also the entries that are not > > > consumed by I-P engine yet, and may be reprocessed by I-P engine. Is that > > > desired? > > > > Yes there's some redundancy in how this is being implemented in this > > revision. I'll split it into two separate, initially duplicate, lists > > for engine and db as you suggested. > > > Thanks, > Han > > > > > > > > > > + ovs_mutex_unlock(&pinctrl_mutex); > > > > + return ap_changed; > > > > +} > > > > + > > > > > > Thanks, > > > Han > > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev