+/* If 'cls_rule' is an OpenFlow rule, that has expired according to OpenFlow
+ * rules, then delete it entirely.
+ *
+ * If 'cls_rule' is a subrule, that has not been used recently, remove it from
+ * the datapath and fold its statistics back into its super-rule.
+ *
+ * (This is a callback function for classifier_for_each().) */
+static void
+rule_expire(struct cls_rule *cls_rule, void *cbdata_)
+{
+ struct expire_cbdata *cbdata = cbdata_;
+ struct ofproto *ofproto = cbdata->ofproto;
+ struct rule *rule = rule_from_cls_rule(cls_rule);
+ long long int hard_expire, idle_expire, expire, now;
+
+ /* Calculate OpenFlow expiration times for 'rule'. */
+ hard_expire = (rule->hard_timeout
+ ? rule->created + rule->hard_timeout * 1000
+ : LLONG_MAX);
+ idle_expire = (rule->idle_timeout
+ && (rule->super || list_is_empty(&rule->list))
+ ? rule->used + rule->idle_timeout * 1000
+ : LLONG_MAX);
+ expire = MIN(hard_expire, idle_expire);
+
+ now = time_msec();
+ if (now < expire) {
+ /* 'rule' has not expired according to OpenFlow rules. */
+ if (!rule->cr.wc.wildcards) {
+ if (now >= rule->used + cbdata->dp_max_idle) {
+ /* This rule is idle, so drop it to free up resources. */
+ if (rule->super) {
+ /* It's not part of the OpenFlow flow table, so we can
+ * delete it entirely and fold its statistics into its
+ * super-rule. */
+ rule_remove(ofproto, rule);
+ } else {
+ /* It is part of the OpenFlow flow table, so we have to
+ * keep the rule but we can at least uninstall it from the
+ * datapath. */
+ rule_uninstall(ofproto, rule);
+ }
+ } else {
+ /* Send NetFlow active timeout if appropriate. */
+ rule_active_timeout(cbdata->ofproto, rule);
+ }
+ }
+ } else {
+ /* 'rule' has expired according to OpenFlow rules. */
+ COVERAGE_INC(ofproto_expired);
+
+ /* Update stats. (This is a no-op if the rule expired due to an idle
+ * timeout, because that only happens when the rule has no subrules
+ * left.) */
+ if (rule->cr.wc.wildcards) {
+ struct rule *subrule, *next;
+ LIST_FOR_EACH_SAFE (subrule, next, list, &rule->list) {
+ rule_remove(cbdata->ofproto, subrule);
+ }
+ } else {
+ rule_uninstall(cbdata->ofproto, rule);
+ }
+
+ /* Get rid of the rule. */
+ if (!rule_is_hidden(rule)) {
+ send_flow_removed(cbdata->ofproto, rule,
+ (now >= hard_expire
+ ? OFPRR_HARD_TIMEOUT : OFPRR_IDLE_TIMEOUT));
+ }
+ rule_remove(cbdata->ofproto, rule);
+ }
+}
+\f
+static void
+revalidate_cb(struct cls_rule *sub_, void *cbdata_)
+{
+ struct rule *sub = rule_from_cls_rule(sub_);
+ struct revalidate_cbdata *cbdata = cbdata_;
+
+ if (cbdata->revalidate_all
+ || (cbdata->revalidate_subrules && sub->super)
+ || (tag_set_intersects(&cbdata->revalidate_set, sub->tags))) {
+ revalidate_rule(cbdata->ofproto, sub);
+ }
+}
+
+static bool
+revalidate_rule(struct ofproto *p, struct rule *rule)
+{
+ const struct flow *flow = &rule->cr.flow;
+
+ COVERAGE_INC(ofproto_revalidate_rule);
+ if (rule->super) {
+ struct rule *super;
+ super = rule_from_cls_rule(classifier_lookup(&p->cls, flow,
+ CLS_INC_WILD));
+ if (!super) {
+ rule_remove(p, rule);
+ return false;
+ } else if (super != rule->super) {
+ COVERAGE_INC(ofproto_revalidate_moved);
+ list_remove(&rule->list);
+ list_push_back(&super->list, &rule->list);
+ rule->super = super;
+ rule->hard_timeout = super->hard_timeout;
+ rule->idle_timeout = super->idle_timeout;
+ rule->created = super->created;
+ rule->used = 0;
+ }