When the a revalidator thread is updating statistics for an XC_LEARN
xcache entry in xlate_push_stats_entry it uses ofproto_flow_mod_learn.
The revalidator will update stats for rules even if they are in a
removed state or marked as invisible. However, ofproto_flow_mod_learn
will detect if a flow has been removed and re-add it in that case. This
can result in an old learn action replacing the new learn action that
had replaced it in the first place.

This change adds a new force parameter to ofproto_flow_mod_learn
allowing the caller to specify an action to take if temp_rule is
removed. If force is set to false and the rule has been replaced in the
classifier with a more recent rule, then ofproto_flow_mod_learn will
just return.

Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2213892
Signed-off-by: Mike Pattrick <m...@redhat.com>
---
v2: Added additional checks if rule is removed
v3: v2 patch was corrupted in transit
---
 ofproto/ofproto-dpif-xlate-cache.c |  2 +-
 ofproto/ofproto-dpif-xlate.c       |  2 +-
 ofproto/ofproto-dpif.c             |  2 +-
 ofproto/ofproto-provider.h         |  6 ++--
 ofproto/ofproto.c                  | 48 +++++++++++++++++++++++++-----
 5 files changed, 47 insertions(+), 13 deletions(-)

diff --git a/ofproto/ofproto-dpif-xlate-cache.c 
b/ofproto/ofproto-dpif-xlate-cache.c
index 9224ee2e6..843903614 100644
--- a/ofproto/ofproto-dpif-xlate-cache.c
+++ b/ofproto/ofproto-dpif-xlate-cache.c
@@ -125,7 +125,7 @@ xlate_push_stats_entry(struct xc_entry *entry,
     case XC_LEARN: {
         enum ofperr error;
         error = ofproto_flow_mod_learn(entry->learn.ofm, true,
-                                       entry->learn.limit, NULL);
+                                       entry->learn.limit, NULL, false);
         if (error) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
             VLOG_WARN_RL(&rl, "xcache LEARN action execution failed.");
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 47ea0f47e..144938855 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -5701,7 +5701,7 @@ xlate_learn_action(struct xlate_ctx *ctx, const struct 
ofpact_learn *learn)
             bool success = true;
             if (ctx->xin->allow_side_effects) {
                 error = ofproto_flow_mod_learn(ofm, ctx->xin->xcache != NULL,
-                                               learn->limit, &success);
+                                               learn->limit, &success, true);
             } else if (learn->limit) {
                 if (!ofm->temp_rule
                     || ofm->temp_rule->state != RULE_INSERTED) {
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index e22ca757a..9eb276108 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -4880,7 +4880,7 @@ packet_xlate(struct ofproto *ofproto_, struct 
ofproto_packet_out *opo)
             if (entry->type == XC_LEARN) {
                 struct ofproto_flow_mod *ofm = entry->learn.ofm;

-                error = ofproto_flow_mod_learn_refresh(ofm);
+                error = ofproto_flow_mod_learn_refresh(ofm, true);
                 if (error) {
                     goto error_out;
                 }
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 143ded690..8ac85ea71 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -2027,9 +2027,11 @@ enum ofperr ofproto_flow_mod_init_for_learn(struct 
ofproto *,
                                             struct ofproto_flow_mod *)
     OVS_EXCLUDED(ofproto_mutex);
 enum ofperr ofproto_flow_mod_learn(struct ofproto_flow_mod *, bool keep_ref,
-                                   unsigned limit, bool *below_limit)
+                                   unsigned limit, bool *below_limit,
+                                   bool force)
     OVS_EXCLUDED(ofproto_mutex);
-enum ofperr ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm);
+enum ofperr ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm,
+                                           bool force);
 enum ofperr ofproto_flow_mod_learn_start(struct ofproto_flow_mod *ofm)
     OVS_REQUIRES(ofproto_mutex);
 void ofproto_flow_mod_learn_revert(struct ofproto_flow_mod *ofm)
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index dbf4958bc..611823119 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -5472,7 +5472,7 @@ ofproto_flow_mod_init_for_learn(struct ofproto *ofproto,
 }

 enum ofperr
-ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm)
+ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm, bool force)
 {
     enum ofperr error = 0;

@@ -5494,8 +5494,37 @@ ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod 
*ofm)
     if (rule->state == RULE_REMOVED) {
         struct cls_rule cr;

-        cls_rule_clone(&cr, &rule->cr);
         ovs_mutex_lock(&rule->mutex);
+        if (!force) {
+            /* If the caller hasn't explicitly requested rule recreation and
+             * the rule is removed and not visible, and the classifier has a
+             * newer version of this rule, then don't reinsert it. */
+            struct oftable *table = &rule->ofproto->tables[rule->table_id];
+            ovs_version_t tables_version = rule->ofproto->tables_version;
+
+            if (!cls_rule_visible_in_version(&rule->cr, tables_version)) {
+                const struct cls_rule * curr_cls_rule;
+
+                curr_cls_rule = classifier_find_rule_exactly(&table->cls,
+                                                             &rule->cr,
+                                                             tables_version);
+                if (curr_cls_rule) {
+                    struct rule *curr_rule = rule_from_cls_rule(curr_cls_rule);
+
+                    ovs_mutex_lock(&curr_rule->mutex);
+                    if (curr_rule->state != RULE_REMOVED &&
+                        curr_rule->modified > rule->modified) {
+                        ovs_mutex_unlock(&curr_rule->mutex);
+                        ovs_mutex_unlock(&rule->mutex);
+
+                        return OFPERR_OFPFMFC_UNKNOWN;
+                    }
+                    ovs_mutex_unlock(&curr_rule->mutex);
+                }
+            }
+        }
+
+        cls_rule_clone(&cr, &rule->cr);
         error = ofproto_rule_create(rule->ofproto, &cr, rule->table_id,
                                     rule->flow_cookie,
                                     rule->idle_timeout,
@@ -5565,10 +5594,13 @@ ofproto_flow_mod_learn_finish(struct ofproto_flow_mod 
*ofm,

 /* Refresh 'ofm->temp_rule', for which the caller holds a reference, if already
  * in the classifier, insert it otherwise.  If the rule has already been
- * removed from the classifier, a new rule is created using 'ofm->temp_rule' as
- * a template and the reference to the old 'ofm->temp_rule' is freed.  If
- * 'keep_ref' is true, then a reference to the current rule is held, otherwise
- * it is released and 'ofm->temp_rule' is set to NULL.
+ * removed from the classifier and 'force' is true, a new rule is created
+ * using 'ofm->temp_rule' as a template and the reference to the old
+ * 'ofm->temp_rule' is freed.  If force is false, and the rule has been
+ * removed, and there is a newer copy of that rule in the classifier, do not
+ * reinsert a new copy of that rule. If 'keep_ref' is true, then a reference
+ * to the current rule is held, otherwise it is released and
+ * 'ofm->temp_rule' is set to NULL.
  *
  * If 'limit' != 0, insertion will fail if there are more than 'limit' rules
  * in the same table with the same cookie.  If insertion succeeds,
@@ -5579,10 +5611,10 @@ ofproto_flow_mod_learn_finish(struct ofproto_flow_mod 
*ofm,
  * during the call. */
 enum ofperr
 ofproto_flow_mod_learn(struct ofproto_flow_mod *ofm, bool keep_ref,
-                       unsigned limit, bool *below_limitp)
+                       unsigned limit, bool *below_limitp, bool force)
     OVS_EXCLUDED(ofproto_mutex)
 {
-    enum ofperr error = ofproto_flow_mod_learn_refresh(ofm);
+    enum ofperr error = ofproto_flow_mod_learn_refresh(ofm, force);
     struct rule *rule = ofm->temp_rule;
     bool below_limit = true;

--
2.39.3

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to