X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=23ebae3bfb56356805b147d937b811bad0180e25;hb=75ae71da6b2fe14e5d8b3e2a4c39019bf5d7ee29;hp=68b2493def2686c41dd661d8a8c9c430dd919808;hpb=ca0f572cfe87f284018e14fa7f1de58fbaef4c87;p=openvswitch diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 68b2493d..23ebae3b 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -324,11 +324,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 int 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_); @@ -532,7 +531,7 @@ find_controller_by_target(struct ofproto *ofproto, const char *target) { struct ofconn *ofconn; - HMAP_FOR_EACH_WITH_HASH (ofconn, struct ofconn, hmap_node, + HMAP_FOR_EACH_WITH_HASH (ofconn, hmap_node, hash_string(target, 0), &ofproto->controllers) { if (!strcmp(ofconn_get_target(ofconn), target)) { return ofconn; @@ -557,7 +556,7 @@ update_in_band_remotes(struct ofproto *ofproto) /* Add all the remotes. */ discovery = false; - HMAP_FOR_EACH (ofconn, struct ofconn, hmap_node, &ofproto->controllers) { + HMAP_FOR_EACH (ofconn, hmap_node, &ofproto->controllers) { struct sockaddr_in *sin = &addrs[n_addrs]; if (ofconn->band == OFPROTO_OUT_OF_BAND) { @@ -616,7 +615,7 @@ update_fail_open(struct ofproto *p) n = 0; rconns = xmalloc(hmap_count(&p->controllers) * sizeof *rconns); - HMAP_FOR_EACH (ofconn, struct ofconn, hmap_node, &p->controllers) { + HMAP_FOR_EACH (ofconn, hmap_node, &p->controllers) { rconns[n++] = ofconn->rconn; } @@ -665,8 +664,7 @@ ofproto_set_controllers(struct ofproto *p, /* Delete controllers that are no longer configured. * Update configuration of all now-existing controllers. */ ss_exists = false; - HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, hmap_node, - &p->controllers) { + HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, hmap_node, &p->controllers) { struct ofproto_controller *c; c = shash_find_data(&new_controllers, ofconn_get_target(ofconn)); @@ -682,8 +680,7 @@ ofproto_set_controllers(struct ofproto *p, /* Delete services that are no longer configured. * Update configuration of all now-existing services. */ - HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, struct ofservice, node, - &p->services) { + HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, node, &p->services) { struct ofproto_controller *c; c = shash_find_data(&new_controllers, @@ -722,7 +719,7 @@ ofproto_reconnect_controllers(struct ofproto *ofproto) { struct ofconn *ofconn; - LIST_FOR_EACH (ofconn, struct ofconn, node, &ofproto->all_conns) { + LIST_FOR_EACH (ofconn, node, &ofproto->all_conns) { rconn_reconnect(ofconn->rconn); } } @@ -890,7 +887,7 @@ ofproto_set_sflow(struct ofproto *ofproto, os = ofproto->sflow = ofproto_sflow_create(ofproto->dpif); refresh_port_groups(ofproto); - HMAP_FOR_EACH (ofport, struct ofport, hmap_node, &ofproto->ports) { + HMAP_FOR_EACH (ofport, hmap_node, &ofproto->ports) { ofproto_sflow_add_port(os, ofport->odp_port, netdev_get_name(ofport->netdev)); } @@ -953,16 +950,14 @@ ofproto_destroy(struct ofproto *p) ofproto_flush_flows(p); classifier_destroy(&p->cls); - LIST_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, node, - &p->all_conns) { + LIST_FOR_EACH_SAFE (ofconn, next_ofconn, node, &p->all_conns) { ofconn_destroy(ofconn); } hmap_destroy(&p->controllers); dpif_close(p->dpif); netdev_monitor_destroy(p->netdev_monitor); - HMAP_FOR_EACH_SAFE (ofport, next_ofport, struct ofport, hmap_node, - &p->ports) { + HMAP_FOR_EACH_SAFE (ofport, next_ofport, hmap_node, &p->ports) { hmap_remove(&p->ports, &ofport->hmap_node); ofport_free(ofport); } @@ -972,8 +967,7 @@ ofproto_destroy(struct ofproto *p) netflow_destroy(p->netflow); ofproto_sflow_destroy(p->sflow); - HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, struct ofservice, node, - &p->services) { + HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, node, &p->services) { ofservice_destroy(p, ofservice); } hmap_destroy(&p->services); @@ -1045,7 +1039,7 @@ add_snooper(struct ofproto *ofproto, struct vconn *vconn) /* Pick a controller for monitoring. */ best = NULL; - LIST_FOR_EACH (ofconn, struct ofconn, node, &ofproto->all_conns) { + LIST_FOR_EACH (ofconn, node, &ofproto->all_conns) { if (ofconn->type == OFCONN_PRIMARY && (!best || snoop_preference(ofconn) > snoop_preference(best))) { best = ofconn; @@ -1108,8 +1102,7 @@ ofproto_run1(struct ofproto *p) in_band_run(p->in_band); } - LIST_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, node, - &p->all_conns) { + LIST_FOR_EACH_SAFE (ofconn, next_ofconn, node, &p->all_conns) { ofconn_run(ofconn, p); } @@ -1119,7 +1112,7 @@ ofproto_run1(struct ofproto *p) fail_open_run(p->fail_open); } - HMAP_FOR_EACH (ofservice, struct ofservice, node, &p->services) { + HMAP_FOR_EACH (ofservice, node, &p->services) { struct vconn *vconn; int retval; @@ -1154,19 +1147,9 @@ ofproto_run1(struct ofproto *p) } if (time_msec() >= p->next_expiration) { + int delay = ofproto_expire(p); + p->next_expiration = time_msec() + delay; COVERAGE_INC(ofproto_expiration); - 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) { @@ -1215,7 +1198,7 @@ ofproto_wait(struct ofproto *p) dpif_recv_wait(p->dpif); dpif_port_poll_wait(p->dpif); netdev_monitor_poll_wait(p->netdev_monitor); - LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) { + LIST_FOR_EACH (ofconn, node, &p->all_conns) { ofconn_wait(ofconn); } if (p->in_band) { @@ -1238,7 +1221,7 @@ ofproto_wait(struct ofproto *p) } else if (p->next_expiration != LLONG_MAX) { poll_timer_wait_until(p->next_expiration); } - HMAP_FOR_EACH (ofservice, struct ofservice, node, &p->services) { + HMAP_FOR_EACH (ofservice, node, &p->services) { pvconn_wait(ofservice->pvconn); } for (i = 0; i < p->n_snoops; i++) { @@ -1352,7 +1335,7 @@ reinit_ports(struct ofproto *p) size_t i; svec_init(&devnames); - HMAP_FOR_EACH (ofport, struct ofport, hmap_node, &p->ports) { + HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { svec_add (&devnames, (char *) ofport->opp.name); } dpif_port_list(p->dpif, &odp_ports, &n_odp_ports); @@ -1379,7 +1362,7 @@ refresh_port_group(struct ofproto *p, unsigned int group) ports = xmalloc(hmap_count(&p->ports) * sizeof *ports); n_ports = 0; - HMAP_FOR_EACH (port, struct ofport, hmap_node, &p->ports) { + HMAP_FOR_EACH (port, hmap_node, &p->ports) { if (group == DP_GROUP_ALL || !(port->opp.config & OFPPC_NO_FLOOD)) { ports[n_ports++] = port->odp_port; } @@ -1484,7 +1467,7 @@ send_port_status(struct ofproto *p, const struct ofport *ofport, { /* XXX Should limit the number of queued port status change messages. */ struct ofconn *ofconn; - LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) { + LIST_FOR_EACH (ofconn, node, &p->all_conns) { struct ofp_port_status *ops; struct ofpbuf *b; @@ -1542,7 +1525,7 @@ get_port(const struct ofproto *ofproto, uint16_t odp_port) { struct ofport *port; - HMAP_FOR_EACH_IN_BUCKET (port, struct ofport, hmap_node, + HMAP_FOR_EACH_IN_BUCKET (port, hmap_node, hash_int(odp_port, 0), &ofproto->ports) { if (port->odp_port == odp_port) { return port; @@ -1848,8 +1831,8 @@ ofservice_lookup(struct ofproto *ofproto, const char *target) { struct ofservice *ofservice; - HMAP_FOR_EACH_WITH_HASH (ofservice, struct ofservice, node, - hash_string(target, 0), &ofproto->services) { + HMAP_FOR_EACH_WITH_HASH (ofservice, node, hash_string(target, 0), + &ofproto->services) { if (!strcmp(pvconn_get_name(ofservice->pvconn), target)) { return ofservice; } @@ -1877,8 +1860,10 @@ rule_create(struct ofproto *ofproto, struct rule *super, } else { list_init(&rule->list); } - rule->n_actions = n_actions; - rule->actions = xmemdup(actions, n_actions * sizeof *actions); + if (n_actions > 0) { + rule->n_actions = n_actions; + rule->actions = xmemdup(actions, n_actions * sizeof *actions); + } netflow_flow_clear(&rule->nf_flow); netflow_flow_update_time(ofproto->netflow, &rule->nf_flow, rule->created); @@ -1912,7 +1897,7 @@ rule_destroy(struct ofproto *ofproto, struct rule *rule) { if (!rule->super) { struct rule *subrule, *next; - LIST_FOR_EACH_SAFE (subrule, next, struct rule, list, &rule->list) { + LIST_FOR_EACH_SAFE (subrule, next, list, &rule->list) { revalidate_rule(ofproto, subrule); } } else { @@ -2087,6 +2072,17 @@ rule_create_subrule(struct ofproto *ofproto, struct rule *rule, return subrule; } +/* Remove 'rule' from 'ofproto' and free up the associated memory: + * + * - If 'rule' was installed in the datapath, uninstalls it and updates + * 'rule''s statistics (or its super-rule's statistics, if it is a + * subrule), via rule_uninstall(). + * + * - Removes 'rule' from the classifier. + * + * - If 'rule' is a super-rule that has subrules, revalidates (and possibly + * uninstalls and destroys) its subrules, via rule_destroy(). + */ static void rule_remove(struct ofproto *ofproto, struct rule *rule) { @@ -2224,6 +2220,14 @@ rule_account(struct ofproto *ofproto, struct rule *rule, uint64_t extra_bytes) } } +/* 'rule' must be an exact-match rule in 'p'. + * + * If 'rule' is installed in the datapath, uninstalls it and updates's + * statistics. If 'rule' is a subrule, the statistics that are updated are + * actually its super-rule's statistics; otherwise 'rule''s own statistics are + * updated. + * + * If 'rule' is not installed, this function has no effect. */ static void rule_uninstall(struct ofproto *p, struct rule *rule) { @@ -2374,7 +2378,7 @@ handle_features_request(struct ofproto *p, struct ofconn *ofconn, (1u << OFPAT_SET_TP_DST) | (1u << OFPAT_ENQUEUE)); - HMAP_FOR_EACH (port, struct ofport, hmap_node, &p->ports) { + HMAP_FOR_EACH (port, hmap_node, &p->ports) { hton_ofp_phy_port(ofpbuf_put(buf, &port->opp, sizeof port->opp)); } @@ -2667,12 +2671,33 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx, } } +static void +xlate_set_queue_action(struct action_xlate_ctx *ctx, + const struct nx_action_set_queue *nasq) +{ + uint32_t priority; + int error; + + error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(nasq->queue_id), + &priority); + if (error) { + /* Couldn't translate queue to a priority, so ignore. A warning + * has already been logged. */ + return; + } + + remove_pop_action(ctx); + odp_actions_add(ctx->out, ODPAT_SET_PRIORITY)->priority.priority + = priority; +} + static void xlate_nicira_action(struct action_xlate_ctx *ctx, const struct nx_action_header *nah) { const struct nx_action_resubmit *nar; const struct nx_action_set_tunnel *nast; + const struct nx_action_set_queue *nasq; union odp_action *oa; int subtype = ntohs(nah->subtype); @@ -2695,6 +2720,15 @@ xlate_nicira_action(struct action_xlate_ctx *ctx, } break; + case NXAST_SET_QUEUE: + nasq = (const struct nx_action_set_queue *) nah; + xlate_set_queue_action(ctx, nasq); + break; + + case NXAST_POP_QUEUE: + odp_actions_add(ctx->out, ODPAT_POP_PRIORITY); + break; + /* If you add a new action here that modifies flow data, don't forget to * update the flow key in ctx->flow at the same time. */ @@ -3028,17 +3062,6 @@ handle_desc_stats_request(struct ofproto *p, struct ofconn *ofconn, return 0; } -static void -count_subrules(struct cls_rule *cls_rule, void *n_subrules_) -{ - struct rule *rule = rule_from_cls_rule(cls_rule); - int *n_subrules = n_subrules_; - - if (rule->super) { - (*n_subrules)++; - } -} - static int handle_table_stats_request(struct ofproto *p, struct ofconn *ofconn, struct ofp_stats_request *request) @@ -3047,12 +3070,17 @@ handle_table_stats_request(struct ofproto *p, struct ofconn *ofconn, struct ofpbuf *msg; struct odp_stats dpstats; int n_exact, n_subrules, n_wild; + struct rule *rule; msg = start_stats_reply(request, sizeof *ots * 2); /* Count rules of various kinds. */ n_subrules = 0; - classifier_for_each(&p->cls, CLS_INC_EXACT, count_subrules, &n_subrules); + CLASSIFIER_FOR_EACH_EXACT_RULE (rule, cr, &p->cls) { + if (rule->super) { + n_subrules++; + } + } n_exact = classifier_count_exact(&p->cls) - n_subrules; n_wild = classifier_count(&p->cls) - classifier_count_exact(&p->cls); @@ -3136,7 +3164,7 @@ handle_port_stats_request(struct ofproto *p, struct ofconn *ofconn, append_port_stat(port, ofconn, &msg); } } else { - HMAP_FOR_EACH (port, struct ofport, hmap_node, &p->ports) { + HMAP_FOR_EACH (port, hmap_node, &p->ports) { append_port_stat(port, ofconn, &msg); } } @@ -3181,7 +3209,7 @@ query_stats(struct ofproto *p, struct rule *rule, odp_flows = xzalloc(n_odp_flows * sizeof *odp_flows); if (rule->cr.wc.wildcards) { size_t i = 0; - LIST_FOR_EACH (subrule, struct rule, list, &rule->list) { + LIST_FOR_EACH (subrule, list, &rule->list) { odp_flows[i++].key = subrule->cr.flow; packet_count += subrule->packet_count; byte_count += subrule->byte_count; @@ -3242,7 +3270,9 @@ flow_stats_cb(struct cls_rule *rule_, void *cbdata_) memset(ofs->pad2, 0, sizeof ofs->pad2); ofs->packet_count = htonll(packet_count); ofs->byte_count = htonll(byte_count); - memcpy(ofs->actions, rule->actions, act_len); + if (rule->n_actions > 0) { + memcpy(ofs->actions, rule->actions, act_len); + } } static int @@ -3311,7 +3341,9 @@ flow_stats_ds_cb(struct cls_rule *rule_, void *cbdata_) ds_put_format(results, "n_packets=%"PRIu64", ", packet_count); ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count); ofp_print_match(results, &match, true); - ofp_print_actions(results, &rule->actions->header, act_len); + if (act_len > 0) { + ofp_print_actions(results, &rule->actions->header, act_len); + } ds_put_cstr(results, "\n"); } @@ -3439,8 +3471,9 @@ handle_queue_stats_for_port(struct ofport *port, uint32_t queue_id, } else { struct netdev_queue_stats stats; - netdev_get_queue_stats(port->netdev, queue_id, &stats); - put_queue_stats(cbdata, queue_id, &stats); + if (!netdev_get_queue_stats(port->netdev, queue_id, &stats)) { + put_queue_stats(cbdata, queue_id, &stats); + } } } @@ -3468,7 +3501,7 @@ handle_queue_stats_request(struct ofproto *ofproto, struct ofconn *ofconn, port_no = ntohs(qsr->port_no); queue_id = ntohl(qsr->queue_id); if (port_no == OFPP_ALL) { - HMAP_FOR_EACH (port, struct ofport, hmap_node, &ofproto->ports) { + HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) { handle_queue_stats_for_port(port, queue_id, &cbdata); } } else if (port_no < ofproto->max_ports) { @@ -3738,14 +3771,14 @@ modify_flow(struct ofproto *p, const struct ofp_flow_mod *ofm, /* If the actions are the same, do nothing. */ if (n_actions == rule->n_actions - && !memcmp(ofm->actions, rule->actions, actions_len)) + && (!n_actions || !memcmp(ofm->actions, rule->actions, actions_len))) { return 0; } /* Replace actions. */ free(rule->actions); - rule->actions = xmemdup(ofm->actions, actions_len); + rule->actions = n_actions ? xmemdup(ofm->actions, actions_len) : NULL; rule->n_actions = n_actions; /* Make sure that the datapath gets updated properly. */ @@ -3955,8 +3988,7 @@ handle_role_request(struct ofproto *ofproto, if (role == NX_ROLE_MASTER) { struct ofconn *other; - HMAP_FOR_EACH (other, struct ofconn, hmap_node, - &ofproto->controllers) { + HMAP_FOR_EACH (other, hmap_node, &ofproto->controllers) { if (other->role == NX_ROLE_MASTER) { other->role = NX_ROLE_SLAVE; } @@ -4199,6 +4231,285 @@ handle_odp_msg(struct ofproto *p, struct ofpbuf *packet) } } +/* Flow expiration. */ + +struct expire_cbdata { + struct ofproto *ofproto; + int dp_max_idle; +}; + +static int ofproto_dp_max_idle(const struct 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. + * + * Returns the number of milliseconds after which it should be called again. */ +static int +ofproto_expire(struct ofproto *ofproto) +{ + struct expire_cbdata cbdata; + + /* Update 'used' for each flow in the datapath. */ + ofproto_update_used(ofproto); + + /* Expire idle flows. + * + * A wildcarded flow is idle only when all of its subrules have expired due + * to becoming idle, so iterate through the exact-match flows first. */ + cbdata.ofproto = ofproto; + cbdata.dp_max_idle = ofproto_dp_max_idle(ofproto); + classifier_for_each(&ofproto->cls, CLS_INC_EXACT, rule_expire, &cbdata); + classifier_for_each(&ofproto->cls, CLS_INC_WILD, 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); + } + + return MIN(cbdata.dp_max_idle, 1000); +} + +/* 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); +} + +/* Calculates and returns the number of milliseconds of idle time after which + * flows should expire from the datapath and we should fold their statistics + * into their parent rules in userspace. */ +static int +ofproto_dp_max_idle(const struct ofproto *ofproto) +{ + /* + * Idle time histogram. + * + * Most of the time a switch has a relatively small number of flows. When + * this is the case we might as well keep statistics for all of them in + * userspace and to cache them in the kernel datapath for performance as + * well. + * + * As the number of flows increases, the memory required to maintain + * statistics about them in userspace and in the kernel becomes + * significant. However, with a large number of flows it is likely that + * only a few of them are "heavy hitters" that consume a large amount of + * bandwidth. At this point, only heavy hitters are worth caching in the + * kernel and maintaining in userspaces; other flows we can discard. + * + * The technique used to compute the idle time is to build a histogram with + * N_BUCKETS bucket whose width is BUCKET_WIDTH msecs each. Each flow that + * is installed in the kernel gets dropped in the appropriate bucket. + * After the histogram has been built, we compute the cutoff so that only + * the most-recently-used 1% of flows (but at least 1000 flows) are kept + * cached. At least the most-recently-used bucket of flows is kept, so + * actually an arbitrary number of flows can be kept in any given + * expiration run (though the next run will delete most of those unless + * they receive additional data). + * + * This requires a second pass through the exact-match flows, in addition + * to the pass made by ofproto_update_used(), because the former function + * never looks at uninstallable flows. + */ + enum { BUCKET_WIDTH = ROUND_UP(100, TIME_UPDATE_INTERVAL) }; + enum { N_BUCKETS = 5000 / BUCKET_WIDTH }; + int buckets[N_BUCKETS] = { 0 }; + int total, bucket; + struct rule *rule; + long long int now; + int i; + + total = classifier_count_exact(&ofproto->cls); + if (total <= 1000) { + return N_BUCKETS * BUCKET_WIDTH; + } + + /* Build histogram. */ + now = time_msec(); + CLASSIFIER_FOR_EACH_EXACT_RULE (rule, cr, &ofproto->cls) { + long long int idle = now - rule->used; + int bucket = (idle <= 0 ? 0 + : idle >= BUCKET_WIDTH * N_BUCKETS ? N_BUCKETS - 1 + : (unsigned int) idle / BUCKET_WIDTH); + buckets[bucket]++; + } + + /* Find the first bucket whose flows should be expired. */ + for (bucket = 0; bucket < N_BUCKETS; bucket++) { + if (buckets[bucket]) { + int subtotal = 0; + do { + subtotal += buckets[bucket++]; + } while (bucket < N_BUCKETS && subtotal < MAX(1000, total / 100)); + break; + } + } + + if (VLOG_IS_DBG_ENABLED()) { + struct ds s; + + ds_init(&s); + ds_put_cstr(&s, "keep"); + for (i = 0; i < N_BUCKETS; i++) { + if (i == bucket) { + ds_put_cstr(&s, ", drop"); + } + if (buckets[i]) { + ds_put_format(&s, " %d:%d", i * BUCKET_WIDTH, buckets[i]); + } + } + VLOG_INFO("%s: %s (msec:count)", + dpif_name(ofproto->dpif), ds_cstr(&s)); + ds_destroy(&s); + } + + return bucket * BUCKET_WIDTH; +} + +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); + } +} + +/* 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, + (now >= hard_expire + ? OFPRR_HARD_TIMEOUT : OFPRR_IDLE_TIMEOUT)); + } + rule_remove(cbdata->ofproto, rule); + } +} + static void revalidate_cb(struct cls_rule *sub_, void *cbdata_) { @@ -4265,19 +4576,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) @@ -4286,6 +4584,10 @@ send_flow_removed(struct ofproto *p, struct rule *rule, struct ofconn *prev; struct ofpbuf *buf = NULL; + if (!rule->send_flow_removed) { + return; + } + /* We limit the maximum number of queued flow expirations it by accounting * them under the counter for replies. That works because preventing * OpenFlow requests from being processed also prevents new flows from @@ -4293,8 +4595,8 @@ send_flow_removed(struct ofproto *p, struct rule *rule, * requests that would not add new flows, so it is imperfect.) */ prev = NULL; - LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) { - if (rule->send_flow_removed && rconn_is_connected(ofconn->rconn) + LIST_FOR_EACH (ofconn, node, &p->all_conns) { + if (rconn_is_connected(ofconn->rconn) && ofconn_receives_async_msgs(ofconn)) { if (prev) { queue_tx(ofpbuf_clone(buf), prev, prev->reply_counter); @@ -4309,122 +4611,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_) @@ -4556,7 +4742,7 @@ send_packet_in(struct ofproto *ofproto, struct ofpbuf *packet) max_len = do_convert_to_packet_in(packet); prev = NULL; - LIST_FOR_EACH (ofconn, struct ofconn, node, &ofproto->all_conns) { + LIST_FOR_EACH (ofconn, node, &ofproto->all_conns) { if (ofconn_receives_async_msgs(ofconn)) { if (prev) { schedule_packet_in(prev, packet, max_len, true);