[ovs-dev] [PATCH RFC ovn 1/3] northd: Use thread-local storage for logical flow matches.

Mark Michelson mmichels at redhat.com
Wed Jun 16 17:56:09 UTC 2021


For this commit, only the most trivial dynamic string matches have been
replaced with a thread-local buffer. This prevents a great deal of
memory allocations and frees.

Follow-up commits will take care of some of the more complex uses of
dynamic string matches.

Signed-off-by: Mark Michelson <mmichels at redhat.com>
---
 northd/ovn-northd.c | 376 +++++++++++++++++++++-----------------------
 1 file changed, 180 insertions(+), 196 deletions(-)

diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index d872f6a3c..6b51d12de 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -56,6 +56,7 @@
 #include "util.h"
 #include "uuid.h"
 #include "openvswitch/vlog.h"
+#include "ovs-thread.h"
 
 VLOG_DEFINE_THIS_MODULE(ovn_northd);
 
@@ -4083,6 +4084,16 @@ static bool use_parallel_build = true;
 
 static struct hashrow_locks lflow_locks;
 
+DEFINE_STATIC_PER_THREAD_DATA(struct ds, match, DS_EMPTY_INITIALIZER);
+
+static struct ds *
+match_get_clear(void)
+{
+    struct ds *match = match_get();
+    ds_clear(match);
+    return match;
+}
+
 /* Adds a row with the specified contents to the Logical_Flow table.
  * Version to use when locking is required.
  */
@@ -4338,21 +4349,21 @@ static void
 build_port_security_nd(struct ovn_port *op, struct hmap *lflows,
                        const struct ovsdb_idl_row *stage_hint)
 {
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get();
 
     for (size_t i = 0; i < op->n_ps_addrs; i++) {
         struct lport_addresses *ps = &op->ps_addrs[i];
 
         bool no_ip = !(ps->n_ipv4_addrs || ps->n_ipv6_addrs);
 
-        ds_clear(&match);
+        ds_clear(match);
         if (ps->n_ipv4_addrs || no_ip) {
-            ds_put_format(&match,
+            ds_put_format(match,
                           "inport == %s && eth.src == %s && arp.sha == %s",
                           op->json_key, ps->ea_s, ps->ea_s);
 
             if (ps->n_ipv4_addrs) {
-                ds_put_cstr(&match, " && arp.spa == {");
+                ds_put_cstr(match, " && arp.spa == {");
                 for (size_t j = 0; j < ps->n_ipv4_addrs; j++) {
                     /* When the netmask is applied, if the host portion is
                      * non-zero, the host can only use the specified
@@ -4360,38 +4371,37 @@ build_port_security_nd(struct ovn_port *op, struct hmap *lflows,
                      * to use any address in the subnet. */
                     if (ps->ipv4_addrs[j].plen == 32
                         || ps->ipv4_addrs[j].addr & ~ps->ipv4_addrs[j].mask) {
-                        ds_put_cstr(&match, ps->ipv4_addrs[j].addr_s);
+                        ds_put_cstr(match, ps->ipv4_addrs[j].addr_s);
                     } else {
-                        ds_put_format(&match, "%s/%d",
+                        ds_put_format(match, "%s/%d",
                                       ps->ipv4_addrs[j].network_s,
                                       ps->ipv4_addrs[j].plen);
                     }
-                    ds_put_cstr(&match, ", ");
+                    ds_put_cstr(match, ", ");
                 }
-                ds_chomp(&match, ' ');
-                ds_chomp(&match, ',');
-                ds_put_cstr(&match, "}");
+                ds_chomp(match, ' ');
+                ds_chomp(match, ',');
+                ds_put_cstr(match, "}");
             }
             ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND,
-                                    90, ds_cstr(&match), "next;", stage_hint);
+                                    90, ds_cstr(match), "next;", stage_hint);
         }
 
         if (ps->n_ipv6_addrs || no_ip) {
-            ds_clear(&match);
-            ds_put_format(&match, "inport == %s && eth.src == %s",
+            ds_clear(match);
+            ds_put_format(match, "inport == %s && eth.src == %s",
                           op->json_key, ps->ea_s);
-            build_port_security_ipv6_nd_flow(&match, ps->ea, ps->ipv6_addrs,
+            build_port_security_ipv6_nd_flow(match, ps->ea, ps->ipv6_addrs,
                                              ps->n_ipv6_addrs);
             ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND,
-                                    90, ds_cstr(&match), "next;", stage_hint);
+                                    90, ds_cstr(match), "next;", stage_hint);
         }
     }
 
-    ds_clear(&match);
-    ds_put_format(&match, "inport == %s && (arp || nd)", op->json_key);
+    ds_clear(match);
+    ds_put_format(match, "inport == %s && (arp || nd)", op->json_key);
     ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 80,
-                            ds_cstr(&match), "drop;", stage_hint);
-    ds_destroy(&match);
+                            ds_cstr(match), "drop;", stage_hint);
 }
 
 /**
@@ -4432,7 +4442,7 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
         }
 
         if (ps->n_ipv4_addrs) {
-            struct ds match = DS_EMPTY_INITIALIZER;
+            struct ds *match = match_get_clear();
             if (pipeline == P_IN) {
                 /* Permit use of the unspecified address for DHCP discovery */
                 struct ds dhcp_match = DS_EMPTY_INITIALIZER;
@@ -4446,11 +4456,11 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
                                         ds_cstr(&dhcp_match), "next;",
                                         stage_hint);
                 ds_destroy(&dhcp_match);
-                ds_put_format(&match, "inport == %s && eth.src == %s"
+                ds_put_format(match, "inport == %s && eth.src == %s"
                               " && ip4.src == {", op->json_key,
                               ps->ea_s);
             } else {
-                ds_put_format(&match, "outport == %s && eth.dst == %s"
+                ds_put_format(match, "outport == %s && eth.dst == %s"
                               " && ip4.dst == {255.255.255.255, 224.0.0.0/4, ",
                               op->json_key, ps->ea_s);
             }
@@ -4464,33 +4474,32 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
                  */
                 if (ps->ipv4_addrs[j].plen == 32
                     || ps->ipv4_addrs[j].addr & ~mask) {
-                    ds_put_format(&match, "%s", ps->ipv4_addrs[j].addr_s);
+                    ds_put_format(match, "%s", ps->ipv4_addrs[j].addr_s);
                     if (pipeline == P_OUT && ps->ipv4_addrs[j].plen != 32) {
                         /* Host is also allowed to receive packets to the
                          * broadcast address in the specified subnet. */
-                        ds_put_format(&match, ", %s",
+                        ds_put_format(match, ", %s",
                                       ps->ipv4_addrs[j].bcast_s);
                     }
                 } else {
                     /* host portion is zero */
-                    ds_put_format(&match, "%s/%d", ps->ipv4_addrs[j].network_s,
+                    ds_put_format(match, "%s/%d", ps->ipv4_addrs[j].network_s,
                                   ps->ipv4_addrs[j].plen);
                 }
-                ds_put_cstr(&match, ", ");
+                ds_put_cstr(match, ", ");
             }
 
             /* Replace ", " by "}". */
-            ds_chomp(&match, ' ');
-            ds_chomp(&match, ',');
-            ds_put_cstr(&match, "}");
+            ds_chomp(match, ' ');
+            ds_chomp(match, ',');
+            ds_put_cstr(match, "}");
             ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
-                                    ds_cstr(&match), "next;",
+                                    ds_cstr(match), "next;",
                                     stage_hint);
-            ds_destroy(&match);
         }
 
         if (ps->n_ipv6_addrs) {
-            struct ds match = DS_EMPTY_INITIALIZER;
+            struct ds *match = match_get_clear();
             if (pipeline == P_IN) {
                 /* Permit use of unspecified address for duplicate address
                  * detection */
@@ -4506,15 +4515,14 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
                                         stage_hint);
                 ds_destroy(&dad_match);
             }
-            ds_put_format(&match, "%s == %s && %s == %s",
+            ds_put_format(match, "%s == %s && %s == %s",
                           port_direction, op->json_key,
                           pipeline == P_IN ? "eth.src" : "eth.dst", ps->ea_s);
-            build_port_security_ipv6_flow(pipeline, &match, ps->ea,
+            build_port_security_ipv6_flow(pipeline, match, ps->ea,
                                           ps->ipv6_addrs, ps->n_ipv6_addrs);
             ovn_lflow_add_with_hint(lflows, op->od, stage, 90,
-                                    ds_cstr(&match), "next;",
+                                    ds_cstr(match), "next;",
                                     stage_hint);
-            ds_destroy(&match);
         }
 
         char *match = xasprintf("%s == %s && %s == %s && ip",
@@ -5122,19 +5130,19 @@ build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
     }
 
     bool ipv4 = IN6_IS_ADDR_V4MAPPED(&lb_vip->vip);
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get_clear();
     char *meter = "", *action;
 
     if (meter_groups && shash_find(meter_groups, "event-elb")) {
         meter = "event-elb";
     }
 
-    ds_put_format(&match, "ip%s.dst == %s && %s",
+    ds_put_format(match, "ip%s.dst == %s && %s",
                   ipv4 ? "4": "6", lb_vip->vip_str, lb->protocol);
 
     char *vip = lb_vip->vip_str;
     if (lb_vip->vip_port) {
-        ds_put_format(&match, " && %s.dst == %u", lb->protocol,
+        ds_put_format(match, " && %s.dst == %u", lb->protocol,
                       lb_vip->vip_port);
         vip = xasprintf("%s%s%s:%u", ipv4 ? "" : "[", lb_vip->vip_str,
                         ipv4 ? "" : "]", lb_vip->vip_port);
@@ -5147,9 +5155,8 @@ build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
                        event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS),
                        meter, vip, lb->protocol,
                        UUID_ARGS(&lb->header_.uuid));
-    ovn_lflow_add_with_hint(lflows, od, pl, 130, ds_cstr(&match), action,
+    ovn_lflow_add_with_hint(lflows, od, pl, 130, ds_cstr(match), action,
                             &lb->header_);
-    ds_destroy(&match);
     if (lb_vip->vip_port) {
         free(vip);
     }
@@ -5270,32 +5277,31 @@ build_pre_stateful(struct ovn_datapath *od, struct hmap *lflows)
 
     const char *lb_protocols[] = {"tcp", "udp", "sctp"};
     struct ds actions = DS_EMPTY_INITIALIZER;
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get();
 
     for (size_t i = 0; i < ARRAY_SIZE(lb_protocols); i++) {
-        ds_clear(&match);
+        ds_clear(match);
         ds_clear(&actions);
-        ds_put_format(&match, REGBIT_CONNTRACK_NAT" == 1 && ip4 && %s",
+        ds_put_format(match, REGBIT_CONNTRACK_NAT" == 1 && ip4 && %s",
                       lb_protocols[i]);
         ds_put_format(&actions, REG_ORIG_DIP_IPV4 " = ip4.dst; "
                                 REG_ORIG_TP_DPORT " = %s.dst; ct_lb;",
                       lb_protocols[i]);
         ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 120,
-                      ds_cstr(&match), ds_cstr(&actions));
+                      ds_cstr(match), ds_cstr(&actions));
 
-        ds_clear(&match);
+        ds_clear(match);
         ds_clear(&actions);
-        ds_put_format(&match, REGBIT_CONNTRACK_NAT" == 1 && ip6 && %s",
+        ds_put_format(match, REGBIT_CONNTRACK_NAT" == 1 && ip6 && %s",
                       lb_protocols[i]);
         ds_put_format(&actions, REG_ORIG_DIP_IPV6 " = ip6.dst; "
                                 REG_ORIG_TP_DPORT " = %s.dst; ct_lb;",
                       lb_protocols[i]);
         ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 120,
-                      ds_cstr(&match), ds_cstr(&actions));
+                      ds_cstr(match), ds_cstr(&actions));
     }
 
     ds_destroy(&actions);
-    ds_destroy(&match);
 
     ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 110,
                   REGBIT_CONNTRACK_NAT" == 1", "ct_lb;");
@@ -5559,7 +5565,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
                                     &acl->header_);
             ds_destroy(&actions);
         } else {
-            struct ds match = DS_EMPTY_INITIALIZER;
+            struct ds *match = match_get_clear();
             struct ds actions = DS_EMPTY_INITIALIZER;
 
             /* Commit the connection tracking entry if it's a new
@@ -5574,14 +5580,14 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
              * by ct_commit in the "stateful" stage) to indicate that the
              * connection should be allowed to resume.
              */
-            ds_put_format(&match, REGBIT_ACL_HINT_ALLOW_NEW " == 1 && (%s)",
+            ds_put_format(match, REGBIT_ACL_HINT_ALLOW_NEW " == 1 && (%s)",
                           acl->match);
             ds_put_cstr(&actions, REGBIT_CONNTRACK_COMMIT" = 1; ");
             build_acl_log(&actions, acl, meter_groups);
             ds_put_cstr(&actions, "next;");
             ovn_lflow_add_with_hint(lflows, od, stage,
                                     acl->priority + OVN_ACL_PRI_OFFSET,
-                                    ds_cstr(&match),
+                                    ds_cstr(match),
                                     ds_cstr(&actions),
                                     &acl->header_);
 
@@ -5591,24 +5597,23 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
              * proceed to the next table. We use this to ensure that this
              * connection is still allowed by the currently defined
              * policy. Match untracked packets too. */
-            ds_clear(&match);
+            ds_clear(match);
             ds_clear(&actions);
-            ds_put_format(&match, REGBIT_ACL_HINT_ALLOW " == 1 && (%s)",
+            ds_put_format(match, REGBIT_ACL_HINT_ALLOW " == 1 && (%s)",
                           acl->match);
 
             build_acl_log(&actions, acl, meter_groups);
             ds_put_cstr(&actions, "next;");
             ovn_lflow_add_with_hint(lflows, od, stage,
                                     acl->priority + OVN_ACL_PRI_OFFSET,
-                                    ds_cstr(&match), ds_cstr(&actions),
+                                    ds_cstr(match), ds_cstr(&actions),
                                     &acl->header_);
 
-            ds_destroy(&match);
             ds_destroy(&actions);
         }
     } else if (!strcmp(acl->action, "drop")
                || !strcmp(acl->action, "reject")) {
-        struct ds match = DS_EMPTY_INITIALIZER;
+        struct ds *match = match_get_clear();
         struct ds actions = DS_EMPTY_INITIALIZER;
 
         /* The implementation of "drop" differs if stateful ACLs are in
@@ -5618,17 +5623,17 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
         if (has_stateful) {
             /* If the packet is not tracked or not part of an established
              * connection, then we can simply reject/drop it. */
-            ds_put_cstr(&match, REGBIT_ACL_HINT_DROP " == 1");
+            ds_put_cstr(match, REGBIT_ACL_HINT_DROP " == 1");
             if (!strcmp(acl->action, "reject")) {
-                build_reject_acl_rules(od, lflows, stage, acl, &match,
+                build_reject_acl_rules(od, lflows, stage, acl, match,
                                        &actions, &acl->header_, meter_groups);
             } else {
-                ds_put_format(&match, " && (%s)", acl->match);
+                ds_put_format(match, " && (%s)", acl->match);
                 build_acl_log(&actions, acl, meter_groups);
                 ds_put_cstr(&actions, "/* drop */");
                 ovn_lflow_add_with_hint(lflows, od, stage,
                                         acl->priority + OVN_ACL_PRI_OFFSET,
-                                        ds_cstr(&match), ds_cstr(&actions),
+                                        ds_cstr(match), ds_cstr(&actions),
                                         &acl->header_);
             }
             /* For an existing connection without ct_label set, we've
@@ -5642,20 +5647,20 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
              * ct_commit() to the "stateful" stage, but since we're
              * rejecting/dropping the packet, we go ahead and do it here.
              */
-            ds_clear(&match);
+            ds_clear(match);
             ds_clear(&actions);
-            ds_put_cstr(&match, REGBIT_ACL_HINT_BLOCK " == 1");
+            ds_put_cstr(match, REGBIT_ACL_HINT_BLOCK " == 1");
             ds_put_cstr(&actions, "ct_commit { ct_label.blocked = 1; }; ");
             if (!strcmp(acl->action, "reject")) {
-                build_reject_acl_rules(od, lflows, stage, acl, &match,
+                build_reject_acl_rules(od, lflows, stage, acl, match,
                                        &actions, &acl->header_, meter_groups);
             } else {
-                ds_put_format(&match, " && (%s)", acl->match);
+                ds_put_format(match, " && (%s)", acl->match);
                 build_acl_log(&actions, acl, meter_groups);
                 ds_put_cstr(&actions, "/* drop */");
                 ovn_lflow_add_with_hint(lflows, od, stage,
                                         acl->priority + OVN_ACL_PRI_OFFSET,
-                                        ds_cstr(&match), ds_cstr(&actions),
+                                        ds_cstr(match), ds_cstr(&actions),
                                         &acl->header_);
             }
         } else {
@@ -5663,7 +5668,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
              * so a "reject/drop" ACL is simply the "reject/drop"
              * logical flow action in all cases. */
             if (!strcmp(acl->action, "reject")) {
-                build_reject_acl_rules(od, lflows, stage, acl, &match,
+                build_reject_acl_rules(od, lflows, stage, acl, match,
                                        &actions, &acl->header_, meter_groups);
             } else {
                 build_acl_log(&actions, acl, meter_groups);
@@ -5674,7 +5679,6 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
                                         &acl->header_);
             }
         }
-        ds_destroy(&match);
         ds_destroy(&actions);
     }
 }
@@ -5889,17 +5893,16 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
             const char *lease_time = smap_get(
                 &od->nbs->ports[i]->dhcpv4_options->options, "lease_time");
             if (server_id && server_mac && lease_time) {
-                struct ds match = DS_EMPTY_INITIALIZER;
+                struct ds *match = match_get_clear();
                 const char *actions =
                     has_stateful ? "ct_commit; next;" : "next;";
-                ds_put_format(&match, "outport == \"%s\" && eth.src == %s "
+                ds_put_format(match, "outport == \"%s\" && eth.src == %s "
                               "&& ip4.src == %s && udp && udp.src == 67 "
                               "&& udp.dst == 68", od->nbs->ports[i]->name,
                               server_mac, server_id);
                 ovn_lflow_add_with_hint(
-                    lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match),
+                    lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(match),
                     actions, &od->nbs->ports[i]->dhcpv4_options->header_);
-                ds_destroy(&match);
             }
         }
 
@@ -5916,17 +5919,16 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
                 char server_ip[INET6_ADDRSTRLEN + 1];
                 ipv6_string_mapped(server_ip, &lla);
 
-                struct ds match = DS_EMPTY_INITIALIZER;
+                struct ds *match = match_get_clear();
                 const char *actions = has_stateful ? "ct_commit; next;" :
                     "next;";
-                ds_put_format(&match, "outport == \"%s\" && eth.src == %s "
+                ds_put_format(match, "outport == \"%s\" && eth.src == %s "
                               "&& ip6.src == %s && udp && udp.src == 547 "
                               "&& udp.dst == 546", od->nbs->ports[i]->name,
                               server_mac, server_ip);
                 ovn_lflow_add_with_hint(
-                    lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match),
+                    lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(match),
                     actions, &od->nbs->ports[i]->dhcpv6_options->header_);
-                ds_destroy(&match);
             }
         }
     }
@@ -6021,7 +6023,7 @@ build_lb_rules(struct ovn_datapath *od, struct hmap *lflows,
                struct ovn_northd_lb *lb)
 {
     struct ds action = DS_EMPTY_INITIALIZER;
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get();
 
     for (size_t i = 0; i < lb->n_vips; i++) {
         struct ovn_lb_vip *lb_vip = &lb->vips[i];
@@ -6029,7 +6031,7 @@ build_lb_rules(struct ovn_datapath *od, struct hmap *lflows,
         const char *ip_match = NULL;
 
         ds_clear(&action);
-        ds_clear(&match);
+        ds_clear(match);
 
         /* Store the original destination IP to be used when generating
          * hairpin flows.
@@ -6066,21 +6068,20 @@ build_lb_rules(struct ovn_datapath *od, struct hmap *lflows,
         build_lb_vip_actions(lb_vip, lb_vip_nb, &action,
                              lb->selection_fields, true);
 
-        ds_put_format(&match, "ct.new && %s.dst == %s", ip_match,
+        ds_put_format(match, "ct.new && %s.dst == %s", ip_match,
                       lb_vip->vip_str);
         if (lb_vip->vip_port) {
-            ds_put_format(&match, " && %s.dst == %d", proto, lb_vip->vip_port);
+            ds_put_format(match, " && %s.dst == %d", proto, lb_vip->vip_port);
             ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_STATEFUL, 120,
-                                    ds_cstr(&match), ds_cstr(&action),
+                                    ds_cstr(match), ds_cstr(&action),
                                     &lb->nlb->header_);
         } else {
             ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_STATEFUL, 110,
-                                    ds_cstr(&match), ds_cstr(&action),
+                                    ds_cstr(match), ds_cstr(&action),
                                     &lb->nlb->header_);
         }
     }
     ds_destroy(&action);
-    ds_destroy(&match);
 }
 
 static void
@@ -6179,7 +6180,7 @@ build_fwd_group_lflows(struct ovn_datapath *od, struct hmap *lflows)
 {
 
     if (!(!od->nbs || !od->nbs->n_forwarding_groups)) {
-        struct ds match = DS_EMPTY_INITIALIZER;
+        struct ds *match = match_get_clear();
         struct ds actions = DS_EMPTY_INITIALIZER;
         struct ds group_ports = DS_EMPTY_INITIALIZER;
 
@@ -6191,7 +6192,7 @@ build_fwd_group_lflows(struct ovn_datapath *od, struct hmap *lflows)
             }
 
             /* ARP responder for the forwarding group's virtual IP */
-            ds_put_format(&match, "arp.tpa == %s && arp.op == 1",
+            ds_put_format(match, "arp.tpa == %s && arp.op == 1",
                           fwd_group->vip);
             ds_put_format(&actions,
                 "eth.dst = eth.src; "
@@ -6207,12 +6208,12 @@ build_fwd_group_lflows(struct ovn_datapath *od, struct hmap *lflows)
                 fwd_group->vmac, fwd_group->vmac, fwd_group->vip);
 
             ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 50,
-                                    ds_cstr(&match), ds_cstr(&actions),
+                                    ds_cstr(match), ds_cstr(&actions),
                                     &fwd_group->header_);
 
             /* L2 lookup for the forwarding group's virtual MAC */
-            ds_clear(&match);
-            ds_put_format(&match, "eth.dst == %s", fwd_group->vmac);
+            ds_clear(match);
+            ds_put_format(match, "eth.dst == %s", fwd_group->vmac);
 
             /* Create a comma separated string of child ports */
             ds_clear(&group_ports);
@@ -6230,11 +6231,10 @@ build_fwd_group_lflows(struct ovn_datapath *od, struct hmap *lflows)
             ds_clear(&actions);
             ds_put_format(&actions, "fwd_group(%s);", ds_cstr(&group_ports));
             ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, 50,
-                                    ds_cstr(&match), ds_cstr(&actions),
+                                    ds_cstr(match), ds_cstr(&actions),
                                     &fwd_group->header_);
         }
 
-        ds_destroy(&match);
         ds_destroy(&actions);
         ds_destroy(&group_ports);
     }
@@ -6390,7 +6390,7 @@ build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op,
 {
     struct sset all_eth_addrs = SSET_INITIALIZER(&all_eth_addrs);
     struct ds eth_src = DS_EMPTY_INITIALIZER;
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get_clear();
 
     sset_add(&all_eth_addrs, op->lrp_networks.ea_s);
 
@@ -6445,15 +6445,14 @@ build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op,
     ds_chomp(&eth_src, ',');
     ds_put_cstr(&eth_src, "}");
 
-    ds_put_format(&match, "eth.src == %s && (arp.op == 1 || nd_ns)",
+    ds_put_format(match, "eth.src == %s && (arp.op == 1 || nd_ns)",
                   ds_cstr(&eth_src));
     ovn_lflow_add_unique(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
-                         ds_cstr(&match),
+                         ds_cstr(match),
                          "outport = \""MC_FLOOD_L2"\"; output;");
 
     sset_destroy(&all_eth_addrs);
     ds_destroy(&eth_src);
-    ds_destroy(&match);
 }
 
 /*
@@ -6470,7 +6469,7 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
                                         struct hmap *lflows,
                                         const struct ovsdb_idl_row *stage_hint)
 {
-    struct ds match   = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get_clear();
     struct ds actions = DS_EMPTY_INITIALIZER;
 
     /* Packets received from VXLAN tunnels have already been through the
@@ -6478,22 +6477,22 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
      * multicast_group implementation (VXLAN packets skip table 32 which
      * delivers to patch ports) but we're bypassing multicast_groups.
      */
-    ds_put_cstr(&match, FLAGBIT_NOT_VXLAN " && ");
+    ds_put_cstr(match, FLAGBIT_NOT_VXLAN " && ");
 
     if (addr_family == AF_INET) {
-        ds_put_cstr(&match, "arp.op == 1 && arp.tpa == { ");
+        ds_put_cstr(match, "arp.op == 1 && arp.tpa == { ");
     } else {
-        ds_put_cstr(&match, "nd_ns && nd.target == { ");
+        ds_put_cstr(match, "nd_ns && nd.target == { ");
     }
 
     const char *ip_address;
     SSET_FOR_EACH (ip_address, ips) {
-        ds_put_format(&match, "%s, ", ip_address);
+        ds_put_format(match, "%s, ", ip_address);
     }
 
-    ds_chomp(&match, ' ');
-    ds_chomp(&match, ',');
-    ds_put_cstr(&match, "}");
+    ds_chomp(match, ' ');
+    ds_chomp(match, ',');
+    ds_put_cstr(match, "}");
 
     /* Send a the packet to the router pipeline.  If the switch has non-router
      * ports then flood it there as well.
@@ -6503,16 +6502,15 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
                                 "outport = \""MC_FLOOD_L2"\"; output;",
                       patch_op->json_key);
         ovn_lflow_add_unique_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP,
-                                       priority, ds_cstr(&match),
+                                       priority, ds_cstr(match),
                                        ds_cstr(&actions), stage_hint);
     } else {
         ds_put_format(&actions, "outport = %s; output;", patch_op->json_key);
         ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
-                                ds_cstr(&match), ds_cstr(&actions),
+                                ds_cstr(match), ds_cstr(&actions),
                                 stage_hint);
     }
 
-    ds_destroy(&match);
     ds_destroy(&actions);
 }
 
@@ -6645,7 +6643,7 @@ build_dhcpv4_options_flows(struct ovn_port *op,
                            const char *json_key, bool is_external,
                            struct hmap *lflows)
 {
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get();
 
     for (size_t j = 0; j < lsp_addrs->n_ipv4_addrs; j++) {
         struct ds options_action = DS_EMPTY_INITIALIZER;
@@ -6654,24 +6652,24 @@ build_dhcpv4_options_flows(struct ovn_port *op,
         if (build_dhcpv4_action(
                 op, lsp_addrs->ipv4_addrs[j].addr,
                 &options_action, &response_action, &ipv4_addr_match)) {
-            ds_clear(&match);
+            ds_clear(match);
             ds_put_format(
-                &match, "inport == %s && eth.src == %s && "
+                match, "inport == %s && eth.src == %s && "
                 "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
                 "udp.src == 68 && udp.dst == 67",
                 json_key, lsp_addrs->ea_s);
 
             if (is_external) {
-                ds_put_format(&match, " && is_chassis_resident(%s)",
+                ds_put_format(match, " && is_chassis_resident(%s)",
                               op->json_key);
             }
 
             ovn_lflow_add_with_hint(lflows, op->od,
                                     S_SWITCH_IN_DHCP_OPTIONS, 100,
-                                    ds_cstr(&match),
+                                    ds_cstr(match),
                                     ds_cstr(&options_action),
                                     &op->nbsp->dhcpv4_options->header_);
-            ds_clear(&match);
+            ds_clear(match);
             /* Allow ip4.src = OFFER_IP and
              * ip4.dst = {SERVER_IP, 255.255.255.255} for the below
              * cases
@@ -6681,38 +6679,38 @@ build_dhcpv4_options_flows(struct ovn_port *op,
              *     broadcasting the DHCPREQUEST.
              */
             ds_put_format(
-                &match, "inport == %s && eth.src == %s && "
+                match, "inport == %s && eth.src == %s && "
                 "%s && udp.src == 68 && udp.dst == 67",
                 json_key, lsp_addrs->ea_s, ds_cstr(&ipv4_addr_match));
 
             if (is_external) {
-                ds_put_format(&match, " && is_chassis_resident(%s)",
+                ds_put_format(match, " && is_chassis_resident(%s)",
                               op->json_key);
             }
 
             ovn_lflow_add_with_hint(lflows, op->od,
                                     S_SWITCH_IN_DHCP_OPTIONS, 100,
-                                    ds_cstr(&match),
+                                    ds_cstr(match),
                                     ds_cstr(&options_action),
                                     &op->nbsp->dhcpv4_options->header_);
-            ds_clear(&match);
+            ds_clear(match);
 
             /* If REGBIT_DHCP_OPTS_RESULT is set, it means the
              * put_dhcp_opts action is successful. */
             ds_put_format(
-                &match, "inport == %s && eth.src == %s && "
+                match, "inport == %s && eth.src == %s && "
                 "ip4 && udp.src == 68 && udp.dst == 67"
                 " && "REGBIT_DHCP_OPTS_RESULT,
                 json_key, lsp_addrs->ea_s);
 
             if (is_external) {
-                ds_put_format(&match, " && is_chassis_resident(%s)",
+                ds_put_format(match, " && is_chassis_resident(%s)",
                               op->json_key);
             }
 
             ovn_lflow_add_with_hint(lflows, op->od,
                                     S_SWITCH_IN_DHCP_RESPONSE, 100,
-                                    ds_cstr(&match),
+                                    ds_cstr(match),
                                     ds_cstr(&response_action),
                                     &op->nbsp->dhcpv4_options->header_);
             ds_destroy(&options_action);
@@ -6721,7 +6719,6 @@ build_dhcpv4_options_flows(struct ovn_port *op,
             break;
         }
     }
-    ds_destroy(&match);
 }
 
 static void
@@ -6730,7 +6727,7 @@ build_dhcpv6_options_flows(struct ovn_port *op,
                            const char *json_key, bool is_external,
                            struct hmap *lflows)
 {
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get();
 
     for (size_t j = 0; j < lsp_addrs->n_ipv6_addrs; j++) {
         struct ds options_action = DS_EMPTY_INITIALIZER;
@@ -6738,30 +6735,30 @@ build_dhcpv6_options_flows(struct ovn_port *op,
         if (build_dhcpv6_action(
                 op, &lsp_addrs->ipv6_addrs[j].addr,
                 &options_action, &response_action)) {
-            ds_clear(&match);
+            ds_clear(match);
             ds_put_format(
-                &match, "inport == %s && eth.src == %s"
+                match, "inport == %s && eth.src == %s"
                 " && ip6.dst == ff02::1:2 && udp.src == 546 &&"
                 " udp.dst == 547",
                 json_key, lsp_addrs->ea_s);
 
             if (is_external) {
-                ds_put_format(&match, " && is_chassis_resident(%s)",
+                ds_put_format(match, " && is_chassis_resident(%s)",
                               op->json_key);
             }
 
             ovn_lflow_add_with_hint(lflows, op->od,
                                     S_SWITCH_IN_DHCP_OPTIONS, 100,
-                                    ds_cstr(&match),
+                                    ds_cstr(match),
                                     ds_cstr(&options_action),
                                     &op->nbsp->dhcpv6_options->header_);
 
             /* If REGBIT_DHCP_OPTS_RESULT is set to 1, it means the
              * put_dhcpv6_opts action is successful */
-            ds_put_cstr(&match, " && "REGBIT_DHCP_OPTS_RESULT);
+            ds_put_cstr(match, " && "REGBIT_DHCP_OPTS_RESULT);
             ovn_lflow_add_with_hint(lflows, op->od,
                                     S_SWITCH_IN_DHCP_RESPONSE, 100,
-                                    ds_cstr(&match),
+                                    ds_cstr(match),
                                     ds_cstr(&response_action),
                                     &op->nbsp->dhcpv6_options->header_);
             ds_destroy(&options_action);
@@ -6769,7 +6766,6 @@ build_dhcpv6_options_flows(struct ovn_port *op,
             break;
         }
     }
-    ds_destroy(&match);
 }
 
 static void
@@ -6777,16 +6773,16 @@ build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op,
                                                  const struct ovn_port *port,
                                                  struct hmap *lflows)
 {
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get();
 
     for (size_t i = 0; i < op->n_lsp_addrs; i++) {
         for (size_t j = 0; j < op->od->n_router_ports; j++) {
             struct ovn_port *rp = op->od->router_ports[j];
             for (size_t k = 0; k < rp->n_lsp_addrs; k++) {
                 for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv4_addrs; l++) {
-                    ds_clear(&match);
+                    ds_clear(match);
                     ds_put_format(
-                        &match, "inport == %s && eth.src == %s"
+                        match, "inport == %s && eth.src == %s"
                         " && !is_chassis_resident(%s)"
                         " && arp.tpa == %s && arp.op == 1",
                         port->json_key,
@@ -6794,13 +6790,13 @@ build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op,
                         rp->lsp_addrs[k].ipv4_addrs[l].addr_s);
                     ovn_lflow_add_with_hint(lflows, op->od,
                                             S_SWITCH_IN_EXTERNAL_PORT,
-                                            100, ds_cstr(&match), "drop;",
+                                            100, ds_cstr(match), "drop;",
                                             &op->nbsp->header_);
                 }
                 for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv6_addrs; l++) {
-                    ds_clear(&match);
+                    ds_clear(match);
                     ds_put_format(
-                        &match, "inport == %s && eth.src == %s"
+                        match, "inport == %s && eth.src == %s"
                         " && !is_chassis_resident(%s)"
                         " && nd_ns && ip6.dst == {%s, %s} && nd.target == %s",
                         port->json_key,
@@ -6810,13 +6806,13 @@ build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op,
                         rp->lsp_addrs[k].ipv6_addrs[l].addr_s);
                     ovn_lflow_add_with_hint(lflows, op->od,
                                             S_SWITCH_IN_EXTERNAL_PORT, 100,
-                                            ds_cstr(&match), "drop;",
+                                            ds_cstr(match), "drop;",
                                             &op->nbsp->header_);
                 }
 
-                ds_clear(&match);
+                ds_clear(match);
                 ds_put_format(
-                    &match, "inport == %s && eth.src == %s"
+                    match, "inport == %s && eth.src == %s"
                     " && eth.dst == %s"
                     " && !is_chassis_resident(%s)",
                     port->json_key,
@@ -6824,12 +6820,11 @@ build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op,
                     op->json_key);
                 ovn_lflow_add_with_hint(lflows, op->od,
                                         S_SWITCH_IN_EXTERNAL_PORT,
-                                        100, ds_cstr(&match), "drop;",
+                                        100, ds_cstr(match), "drop;",
                                         &op->nbsp->header_);
             }
         }
     }
-    ds_destroy(&match);
 }
 
 static bool
@@ -6844,7 +6839,6 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *lflows)
     /* This flow table structure is documented in ovn-northd(8), so please
      * update ovn-northd.8.xml if you change anything. */
 
-    struct ds match = DS_EMPTY_INITIALIZER;
     struct ds actions = DS_EMPTY_INITIALIZER;
     struct ovn_datapath *od;
 
@@ -6869,7 +6863,6 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *lflows)
                       "output;");
     }
 
-    ds_destroy(&match);
     ds_destroy(&actions);
 }
 
@@ -7870,7 +7863,7 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od,
                           const struct nbrec_logical_router_policy *rule,
                           const struct ovsdb_idl_row *stage_hint)
 {
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get_clear();
     struct ds actions = DS_EMPTY_INITIALIZER;
 
     if (!strcmp(rule->action, "reroute")) {
@@ -7921,11 +7914,10 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od,
         }
         ds_put_cstr(&actions, REG_ECMP_GROUP_ID" = 0; next;");
     }
-    ds_put_format(&match, "%s", rule->match);
+    ds_put_format(match, "%s", rule->match);
 
     ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY, rule->priority,
-                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
-    ds_destroy(&match);
+                            ds_cstr(match), ds_cstr(&actions), stage_hint);
     ds_destroy(&actions);
 }
 
@@ -7958,7 +7950,7 @@ build_ecmp_routing_policy_flows(struct hmap *lflows, struct ovn_datapath *od,
         }
     }
 
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get();
     struct ds actions = DS_EMPTY_INITIALIZER;
 
     for (size_t i = 0; i < rule->n_nexthops; i++) {
@@ -7999,12 +7991,12 @@ build_ecmp_routing_policy_flows(struct hmap *lflows, struct ovn_datapath *od,
                       out_port->lrp_networks.ea_s,
                       out_port->json_key);
 
-        ds_clear(&match);
-        ds_put_format(&match, REG_ECMP_GROUP_ID" == %"PRIu16" && "
+        ds_clear(match);
+        ds_put_format(match, REG_ECMP_GROUP_ID" == %"PRIu16" && "
                       REG_ECMP_MEMBER_ID" == %"PRIuSIZE,
                       ecmp_group_id, i + 1);
         ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY_ECMP,
-                                100, ds_cstr(&match),
+                                100, ds_cstr(match),
                                 ds_cstr(&actions), &rule->header_);
     }
 
@@ -8026,7 +8018,6 @@ build_ecmp_routing_policy_flows(struct hmap *lflows, struct ovn_datapath *od,
                             ds_cstr(&actions), &rule->header_);
 
 cleanup:
-    ds_destroy(&match);
     ds_destroy(&actions);
 }
 
@@ -8390,7 +8381,7 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
                                struct ds *route_match)
 {
     const struct nbrec_logical_router_static_route *st_route = route->route;
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get_clear();
     struct ds actions = DS_EMPTY_INITIALIZER;
     struct ds ecmp_reply = DS_EMPTY_INITIALIZER;
     char *cidr = normalize_v46_prefix(&route->prefix, route->plen);
@@ -8398,14 +8389,14 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
     /* If symmetric ECMP replies are enabled, then packets that arrive over
      * an ECMP route need to go through conntrack.
      */
-    ds_put_format(&match, "inport == %s && ip%s.%s == %s",
+    ds_put_format(match, "inport == %s && ip%s.%s == %s",
                   out_port->json_key,
                   IN6_IS_ADDR_V4MAPPED(&route->prefix) ? "4" : "6",
                   route->is_src_route ? "dst" : "src",
                   cidr);
     free(cidr);
     ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DEFRAG, 100,
-                            ds_cstr(&match), "ct_next;",
+                            ds_cstr(match), "ct_next;",
                             &st_route->header_);
 
     /* And packets that go out over an ECMP route need conntrack */
@@ -8419,13 +8410,13 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
      * NOTE: we purposely are not clearing match before this
      * ds_put_cstr() call. The previous contents are needed.
      */
-    ds_put_cstr(&match, " && (ct.new && !ct.est)");
+    ds_put_cstr(match, " && (ct.new && !ct.est)");
 
     ds_put_format(&actions, "ct_commit { ct_label.ecmp_reply_eth = eth.src;"
                   " ct_label.ecmp_reply_port = %" PRId64 ";}; next;",
                   out_port->sb->tunnel_key);
     ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 100,
-                            ds_cstr(&match), ds_cstr(&actions),
+                            ds_cstr(match), ds_cstr(&actions),
                             &st_route->header_);
 
     /* Bypass ECMP selection if we already have ct_label information
@@ -8433,8 +8424,8 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
      */
     ds_put_format(&ecmp_reply, "ct.rpl && ct_label.ecmp_reply_port == %"
                   PRId64, out_port->sb->tunnel_key);
-    ds_clear(&match);
-    ds_put_format(&match, "%s && %s", ds_cstr(&ecmp_reply),
+    ds_clear(match);
+    ds_put_format(match, "%s && %s", ds_cstr(&ecmp_reply),
                   ds_cstr(route_match));
     ds_clear(&actions);
     ds_put_format(&actions, "ip.ttl--; flags.loopback = 1; "
@@ -8443,7 +8434,7 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
                   IN6_IS_ADDR_V4MAPPED(&route->prefix) ? "" : "xx",
                   port_ip, out_port->json_key);
     ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_ROUTING, 300,
-                           ds_cstr(&match), ds_cstr(&actions),
+                           ds_cstr(match), ds_cstr(&actions),
                            &st_route->header_);
 
     /* Egress reply traffic for symmetric ECMP routes skips router policies. */
@@ -8456,7 +8447,6 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
                             200, ds_cstr(&ecmp_reply),
                             action, &st_route->header_);
 
-    ds_destroy(&match);
     ds_destroy(&actions);
     ds_destroy(&ecmp_reply);
 }
@@ -8497,7 +8487,7 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od,
                   ds_cstr(&route_match), ds_cstr(&actions));
 
     /* Add per member flow */
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get();
     struct sset visited_ports = SSET_INITIALIZER(&visited_ports);
     LIST_FOR_EACH (er, list_node, &eg->route_list) {
         const struct parsed_route *route_ = er->route;
@@ -8518,8 +8508,8 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od,
             add_ecmp_symmetric_reply_flows(lflows, od, lrp_addr_s, out_port,
                                            route_, &route_match);
         }
-        ds_clear(&match);
-        ds_put_format(&match, REG_ECMP_GROUP_ID" == %"PRIu16" && "
+        ds_clear(match);
+        ds_put_format(match, REG_ECMP_GROUP_ID" == %"PRIu16" && "
                       REG_ECMP_MEMBER_ID" == %"PRIu16,
                       eg->id, er->id);
         ds_clear(&actions);
@@ -8535,11 +8525,10 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od,
                       out_port->lrp_networks.ea_s,
                       out_port->json_key);
         ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_ROUTING_ECMP, 100,
-                                ds_cstr(&match), ds_cstr(&actions),
+                                ds_cstr(match), ds_cstr(&actions),
                                 &route->header_);
     }
     sset_destroy(&visited_ports);
-    ds_destroy(&match);
     ds_destroy(&route_match);
     ds_destroy(&actions);
 }
@@ -8552,7 +8541,7 @@ add_route(struct hmap *lflows, struct ovn_datapath *od,
           bool is_discard_route)
 {
     bool is_ipv4 = strchr(network_s, '.') ? true : false;
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get_clear();
     uint16_t priority;
     const struct ovn_port *op_inport = NULL;
 
@@ -8565,7 +8554,7 @@ add_route(struct hmap *lflows, struct ovn_datapath *od,
         }
     }
     build_route_match(op_inport, network_s, plen, is_src_route, is_ipv4,
-                      &match, &priority);
+                      match, &priority);
 
     struct ds common_actions = DS_EMPTY_INITIALIZER;
     struct ds actions = DS_EMPTY_INITIALIZER;
@@ -8593,15 +8582,14 @@ add_route(struct hmap *lflows, struct ovn_datapath *od,
     }
 
     ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_ROUTING, priority,
-                            ds_cstr(&match), ds_cstr(&actions),
+                            ds_cstr(match), ds_cstr(&actions),
                             stage_hint);
     if (op && op->has_bfd) {
-        ds_put_format(&match, " && udp.dst == 3784");
+        ds_put_format(match, " && udp.dst == 3784");
         ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_ROUTING,
-                                priority + 1, ds_cstr(&match),
+                                priority + 1, ds_cstr(match),
                                 ds_cstr(&common_actions), stage_hint);
     }
-    ds_destroy(&match);
     ds_destroy(&common_actions);
     ds_destroy(&actions);
 }
@@ -9224,7 +9212,7 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op,
 {
     struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
     const struct nbrec_nat *nat = nat_entry->nb;
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get_clear();
 
     /* Mac address to use when replying to ARP/NS. */
     const char *mac_s = REG_INPORT_ETH_ADDR;
@@ -9240,7 +9228,7 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op,
          * resident, so that upstream MAC learning points to the
          * correct chassis.  Also need to avoid generation of
          * multiple ARP responses from different chassis. */
-        ds_put_format(&match, "is_chassis_resident(\"%s\")",
+        ds_put_format(match, "is_chassis_resident(\"%s\")",
                       nat->logical_port);
     } else {
         mac_s = REG_INPORT_ETH_ADDR;
@@ -9250,7 +9238,7 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op,
          * Also need to avoid generation of multiple ARP responses
          * from different chassis. */
         if (op->od->l3redirect_port) {
-            ds_put_format(&match, "is_chassis_resident(%s)",
+            ds_put_format(match, "is_chassis_resident(%s)",
                           op->od->l3redirect_port->json_key);
         }
     }
@@ -9262,7 +9250,7 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op,
         build_lrouter_nd_flow(op->od, op, "nd_na",
                               ext_addrs->ipv6_addrs[0].addr_s,
                               ext_addrs->ipv6_addrs[0].sn_addr_s,
-                              mac_s, &match, false, 92,
+                              mac_s, match, false, 92,
                               &nat->header_, lflows);
         build_lrouter_nd_flow(op->od, op, "nd_na",
                               ext_addrs->ipv6_addrs[0].addr_s,
@@ -9272,15 +9260,13 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op,
     } else {
         build_lrouter_arp_flow(op->od, op,
                                ext_addrs->ipv4_addrs[0].addr_s,
-                               mac_s, &match, false, 92,
+                               mac_s, match, false, 92,
                                &nat->header_, lflows);
         build_lrouter_arp_flow(op->od, op,
                                ext_addrs->ipv4_addrs[0].addr_s,
                                mac_s, NULL, true, 91,
                                &nat->header_, lflows);
     }
-
-    ds_destroy(&match);
 }
 
 static void
@@ -9347,24 +9333,23 @@ build_lrouter_force_snat_flows(struct hmap *lflows, struct ovn_datapath *od,
                                const char *ip_version, const char *ip_addr,
                                const char *context)
 {
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get_clear();
     struct ds actions = DS_EMPTY_INITIALIZER;
-    ds_put_format(&match, "ip%s && ip%s.dst == %s",
+    ds_put_format(match, "ip%s && ip%s.dst == %s",
                   ip_version, ip_version, ip_addr);
     ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 110,
-                  ds_cstr(&match), "ct_snat;");
+                  ds_cstr(match), "ct_snat;");
 
     /* Higher priority rules to force SNAT with the IP addresses
      * configured in the Gateway router.  This only takes effect
      * when the packet has already been DNATed or load balanced once. */
-    ds_clear(&match);
-    ds_put_format(&match, "flags.force_snat_for_%s == 1 && ip%s",
+    ds_clear(match);
+    ds_put_format(match, "flags.force_snat_for_%s == 1 && ip%s",
                   context, ip_version);
     ds_put_format(&actions, "ct_snat(%s);", ip_addr);
     ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 100,
-                  ds_cstr(&match), ds_cstr(&actions));
+                  ds_cstr(match), ds_cstr(&actions));
 
-    ds_destroy(&match);
     ds_destroy(&actions);
 }
 
@@ -9448,42 +9433,41 @@ build_lrouter_bfd_flows(struct hmap *lflows, struct ovn_port *op)
     }
 
     struct ds ip_list = DS_EMPTY_INITIALIZER;
-    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds *match = match_get_clear();
 
     if (op->lrp_networks.n_ipv4_addrs) {
         op_put_v4_networks(&ip_list, op, false);
-        ds_put_format(&match, "ip4.src == %s && udp.dst == 3784",
+        ds_put_format(match, "ip4.src == %s && udp.dst == 3784",
                       ds_cstr(&ip_list));
         ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110,
-                                ds_cstr(&match), "next; ",
+                                ds_cstr(match), "next; ",
                                 &op->nbrp->header_);
-        ds_clear(&match);
-        ds_put_format(&match, "ip4.dst == %s && udp.dst == 3784",
+        ds_clear(match);
+        ds_put_format(match, "ip4.dst == %s && udp.dst == 3784",
                       ds_cstr(&ip_list));
         ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110,
-                                ds_cstr(&match), "handle_bfd_msg(); ",
+                                ds_cstr(match), "handle_bfd_msg(); ",
                                 &op->nbrp->header_);
     }
     if (op->lrp_networks.n_ipv6_addrs) {
         ds_clear(&ip_list);
-        ds_clear(&match);
+        ds_clear(match);
 
         op_put_v6_networks(&ip_list, op);
-        ds_put_format(&match, "ip6.src == %s && udp.dst == 3784",
+        ds_put_format(match, "ip6.src == %s && udp.dst == 3784",
                       ds_cstr(&ip_list));
         ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110,
-                                ds_cstr(&match), "next; ",
+                                ds_cstr(match), "next; ",
                                 &op->nbrp->header_);
-        ds_clear(&match);
-        ds_put_format(&match, "ip6.dst == %s && udp.dst == 3784",
+        ds_clear(match);
+        ds_put_format(match, "ip6.dst == %s && udp.dst == 3784",
                       ds_cstr(&ip_list));
         ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110,
-                                ds_cstr(&match), "handle_bfd_msg(); ",
+                                ds_cstr(match), "handle_bfd_msg(); ",
                                 &op->nbrp->header_);
     }
 
     ds_destroy(&ip_list);
-    ds_destroy(&match);
 }
 
 /* Logical router ingress Table 0: L2 Admission Control
-- 
2.31.1



More information about the dev mailing list