From 6348e9482369ebd7f1c2d7ec167bd09b9a3d7ecc Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 6 Oct 2010 14:39:56 -0700 Subject: [PATCH] ofproto: Group functions related to flow expiration together. This should be a purely stylistic change, with no effect on behavior. This commit changes the callback pointer passed to the classifier_for_each() from a pointer to an ofproto to a pointer to a structure that includes an ofproto. Future commits planned will add more members to this new structure. --- ofproto/ofproto.c | 321 +++++++++++++++++++++++++--------------------- 1 file changed, 178 insertions(+), 143 deletions(-) diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 844083d8b..552c6e3a9 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -321,11 +321,10 @@ static const struct ofhooks default_ofhooks; static uint64_t pick_datapath_id(const struct ofproto *); static uint64_t pick_fallback_dpid(void); -static void update_used(struct ofproto *); +static void ofproto_expire(struct ofproto *); + static void update_stats(struct ofproto *, struct rule *, const struct odp_flow_stats *); -static void expire_rule(struct cls_rule *, void *ofproto); -static void active_timeout(struct ofproto *ofproto, struct rule *rule); static bool revalidate_rule(struct ofproto *p, struct rule *rule); static void revalidate_cb(struct cls_rule *rule_, void *p_); @@ -1153,18 +1152,8 @@ ofproto_run1(struct ofproto *p) if (time_msec() >= p->next_expiration) { COVERAGE_INC(ofproto_expiration); + ofproto_expire(p); p->next_expiration = time_msec() + 1000; - update_used(p); - - classifier_for_each(&p->cls, CLS_INC_ALL, expire_rule, p); - - /* Let the hook know that we're at a stable point: all outstanding data - * in existing flows has been accounted to the account_cb. Thus, the - * hook can now reasonably do operations that depend on having accurate - * flow volume accounting (currently, that's just bond rebalancing). */ - if (p->ofhooks->account_checkpoint_cb) { - p->ofhooks->account_checkpoint_cb(p->aux); - } } if (p->netflow) { @@ -4192,6 +4181,181 @@ handle_odp_msg(struct ofproto *p, struct ofpbuf *packet) } } +/* Flow expiration. */ + +struct expire_cbdata { + struct ofproto *ofproto; +}; + +static void ofproto_update_used(struct ofproto *); +static void rule_expire(struct cls_rule *, void *cbdata); + +/* This function is called periodically by ofproto_run(). Its job is to + * collect updates for the flows that have been installed into the datapath, + * most importantly when they last were used, and then use that information to + * expire flows that have not been used recently. */ +static void +ofproto_expire(struct ofproto *ofproto) +{ + struct expire_cbdata cbdata; + + /* Update 'used' for each flow in the datapath. */ + ofproto_update_used(ofproto); + + /* Expire idle flows. */ + cbdata.ofproto = ofproto; + classifier_for_each(&ofproto->cls, CLS_INC_ALL, rule_expire, &cbdata); + + /* Let the hook know that we're at a stable point: all outstanding data + * in existing flows has been accounted to the account_cb. Thus, the + * hook can now reasonably do operations that depend on having accurate + * flow volume accounting (currently, that's just bond rebalancing). */ + if (ofproto->ofhooks->account_checkpoint_cb) { + ofproto->ofhooks->account_checkpoint_cb(ofproto->aux); + } +} + +/* Update 'used' member of each flow currently installed into the datapath. */ +static void +ofproto_update_used(struct ofproto *p) +{ + struct odp_flow *flows; + size_t n_flows; + size_t i; + int error; + + error = dpif_flow_list_all(p->dpif, &flows, &n_flows); + if (error) { + return; + } + + for (i = 0; i < n_flows; i++) { + struct odp_flow *f = &flows[i]; + struct rule *rule; + + rule = rule_from_cls_rule( + classifier_find_rule_exactly(&p->cls, &f->key, 0, UINT16_MAX)); + + if (rule && rule->installed) { + update_time(p, rule, &f->stats); + rule_account(p, rule, f->stats.n_bytes); + } else { + /* There's a flow in the datapath that we know nothing about. + * Delete it. */ + COVERAGE_INC(ofproto_unexpected_rule); + dpif_flow_del(p->dpif, f); + } + + } + free(flows); +} + +static void +rule_active_timeout(struct ofproto *ofproto, struct rule *rule) +{ + if (ofproto->netflow && !is_controller_rule(rule) && + netflow_active_timeout_expired(ofproto->netflow, &rule->nf_flow)) { + struct ofexpired expired; + struct odp_flow odp_flow; + + /* Get updated flow stats. + * + * XXX We could avoid this call entirely if (1) ofproto_update_used() + * updated TCP flags and (2) the dpif_flow_list_all() in + * ofproto_update_used() zeroed TCP flags. */ + memset(&odp_flow, 0, sizeof odp_flow); + if (rule->installed) { + odp_flow.key = rule->cr.flow; + odp_flow.flags = ODPFF_ZERO_TCP_FLAGS; + dpif_flow_get(ofproto->dpif, &odp_flow); + + if (odp_flow.stats.n_packets) { + update_time(ofproto, rule, &odp_flow.stats); + netflow_flow_update_flags(&rule->nf_flow, + odp_flow.stats.tcp_flags); + } + } + + expired.flow = rule->cr.flow; + expired.packet_count = rule->packet_count + + odp_flow.stats.n_packets; + expired.byte_count = rule->byte_count + odp_flow.stats.n_bytes; + expired.used = rule->used; + + netflow_expire(ofproto->netflow, &rule->nf_flow, &expired); + + /* Schedule us to send the accumulated records once we have + * collected all of them. */ + poll_immediate_wake(); + } +} + +/* 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->installed && now >= rule->used + 5000) { + /* This rule is idle, so uninstall it from the datapath. */ + if (rule->super) { + rule_remove(ofproto, rule); + } else { + rule_uninstall(ofproto, rule); + } + } else if (!rule->cr.wc.wildcards) { + /* 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, struct rule, 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, + (now >= hard_expire + ? OFPRR_HARD_TIMEOUT : OFPRR_IDLE_TIMEOUT)); + } + rule_remove(cbdata->ofproto, rule); + } +} + static void revalidate_cb(struct cls_rule *sub_, void *cbdata_) { @@ -4258,19 +4422,6 @@ compose_flow_removed(struct ofproto *p, const struct rule *rule, return buf; } -static void -uninstall_idle_flow(struct ofproto *ofproto, struct rule *rule) -{ - assert(rule->installed); - assert(!rule->cr.wc.wildcards); - - if (rule->super) { - rule_remove(ofproto, rule); - } else { - rule_uninstall(ofproto, rule); - } -} - static void send_flow_removed(struct ofproto *p, struct rule *rule, long long int now, uint8_t reason) @@ -4302,122 +4453,6 @@ send_flow_removed(struct ofproto *p, struct rule *rule, } } - -static void -expire_rule(struct cls_rule *cls_rule, void *p_) -{ - struct ofproto *p = p_; - struct rule *rule = rule_from_cls_rule(cls_rule); - long long int hard_expire, idle_expire, expire, now; - - 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) { - if (rule->installed && now >= rule->used + 5000) { - uninstall_idle_flow(p, rule); - } else if (!rule->cr.wc.wildcards) { - active_timeout(p, rule); - } - - return; - } - - COVERAGE_INC(ofproto_expired); - - /* Update stats. This code will be a no-op if the rule expired - * due to an idle timeout. */ - if (rule->cr.wc.wildcards) { - struct rule *subrule, *next; - LIST_FOR_EACH_SAFE (subrule, next, struct rule, list, &rule->list) { - rule_remove(p, subrule); - } - } else { - rule_uninstall(p, rule); - } - - if (!rule_is_hidden(rule)) { - send_flow_removed(p, rule, now, - (now >= hard_expire - ? OFPRR_HARD_TIMEOUT : OFPRR_IDLE_TIMEOUT)); - } - rule_remove(p, rule); -} - -static void -active_timeout(struct ofproto *ofproto, struct rule *rule) -{ - if (ofproto->netflow && !is_controller_rule(rule) && - netflow_active_timeout_expired(ofproto->netflow, &rule->nf_flow)) { - struct ofexpired expired; - struct odp_flow odp_flow; - - /* Get updated flow stats. */ - memset(&odp_flow, 0, sizeof odp_flow); - if (rule->installed) { - odp_flow.key = rule->cr.flow; - odp_flow.flags = ODPFF_ZERO_TCP_FLAGS; - dpif_flow_get(ofproto->dpif, &odp_flow); - - if (odp_flow.stats.n_packets) { - update_time(ofproto, rule, &odp_flow.stats); - netflow_flow_update_flags(&rule->nf_flow, - odp_flow.stats.tcp_flags); - } - } - - expired.flow = rule->cr.flow; - expired.packet_count = rule->packet_count + - odp_flow.stats.n_packets; - expired.byte_count = rule->byte_count + odp_flow.stats.n_bytes; - expired.used = rule->used; - - netflow_expire(ofproto->netflow, &rule->nf_flow, &expired); - - /* Schedule us to send the accumulated records once we have - * collected all of them. */ - poll_immediate_wake(); - } -} - -static void -update_used(struct ofproto *p) -{ - struct odp_flow *flows; - size_t n_flows; - size_t i; - int error; - - error = dpif_flow_list_all(p->dpif, &flows, &n_flows); - if (error) { - return; - } - - for (i = 0; i < n_flows; i++) { - struct odp_flow *f = &flows[i]; - struct rule *rule; - - rule = rule_from_cls_rule( - classifier_find_rule_exactly(&p->cls, &f->key, 0, UINT16_MAX)); - if (!rule || !rule->installed) { - COVERAGE_INC(ofproto_unexpected_rule); - dpif_flow_del(p->dpif, f); - continue; - } - - update_time(p, rule, &f->stats); - rule_account(p, rule, f->stats.n_bytes); - } - free(flows); -} - /* pinsched callback for sending 'packet' on 'ofconn'. */ static void do_send_packet_in(struct ofpbuf *packet, void *ofconn_) -- 2.43.0