X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=341d403d08974e3f40a39b9676591c87ecaa941b;hb=a01ef04ced0d3e04e7d5e5038ef76c861b5c1da3;hp=4b9ceebb738743d5b2fb78236f99b07b0c973b0e;hpb=750638bbefd179b91f8fb3c9c1bde855af05cf45;p=sliver-openvswitch.git diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 4b9ceebb7..341d403d0 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -30,6 +30,8 @@ #include "dpif.h" #include "dynamic-string.h" #include "fail-open.h" +#include "hash.h" +#include "hmap.h" #include "in-band.h" #include "mac-learning.h" #include "netdev.h" @@ -46,7 +48,6 @@ #include "pinsched.h" #include "pktbuf.h" #include "poll-loop.h" -#include "port-array.h" #include "rconn.h" #include "shash.h" #include "status.h" @@ -68,16 +69,19 @@ enum { TABLEID_CLASSIFIER = 1 }; + struct ofport { + struct hmap_node hmap_node; /* In struct ofproto's "ports" hmap. */ struct netdev *netdev; struct ofp_phy_port opp; /* In host byte order. */ + uint16_t odp_port; }; static void ofport_free(struct ofport *); static void hton_ofp_phy_port(struct ofp_phy_port *); static int xlate_actions(const union ofp_action *in, size_t n_in, - const flow_t *flow, struct ofproto *ofproto, + const struct flow *, struct ofproto *, const struct ofpbuf *packet, struct odp_actions *out, tag_type *tags, bool *may_set_up_flow, uint16_t *nf_output_iface); @@ -85,7 +89,7 @@ static int xlate_actions(const union ofp_action *in, size_t n_in, struct rule { struct cls_rule cr; - uint64_t flow_cookie; /* Controller-issued identifier. + uint64_t flow_cookie; /* Controller-issued identifier. (Kept in network-byte order.) */ uint16_t idle_timeout; /* In seconds from time of last use. */ uint16_t hard_timeout; /* In seconds from time of creation. */ @@ -272,8 +276,7 @@ struct ofproto { /* Datapath. */ struct dpif *dpif; struct netdev_monitor *netdev_monitor; - struct port_array ports; /* Index is ODP port nr; ofport->opp.port_no is - * OFP port nr. */ + struct hmap ports; /* Contains "struct ofport"s. */ struct shash port_by_name; uint32_t max_ports; @@ -321,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_); @@ -334,8 +336,7 @@ static void handle_odp_msg(struct ofproto *, struct ofpbuf *); static void handle_openflow(struct ofconn *, struct ofproto *, struct ofpbuf *); -static void refresh_port_groups(struct ofproto *); - +static struct ofport *get_port(const struct ofproto *, uint16_t odp_port); static void update_port(struct ofproto *, const char *devname); static int init_ports(struct ofproto *); static void reinit_ports(struct ofproto *); @@ -388,7 +389,7 @@ ofproto_create(const char *datapath, const char *datapath_type, /* Initialize datapath. */ p->dpif = dpif; p->netdev_monitor = netdev_monitor_create(); - port_array_init(&p->ports); + hmap_init(&p->ports); shash_init(&p->port_by_name); p->max_ports = stats.max_ports; @@ -528,7 +529,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; @@ -553,7 +554,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) { @@ -612,7 +613,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; } @@ -661,8 +662,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)); @@ -678,8 +678,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, @@ -718,7 +717,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); } } @@ -883,12 +882,10 @@ ofproto_set_sflow(struct ofproto *ofproto, if (oso) { if (!os) { struct ofport *ofport; - unsigned int odp_port; os = ofproto->sflow = ofproto_sflow_create(ofproto->dpif); - refresh_port_groups(ofproto); - PORT_ARRAY_FOR_EACH (ofport, &ofproto->ports, odp_port) { - ofproto_sflow_add_port(os, odp_port, + HMAP_FOR_EACH (ofport, hmap_node, &ofproto->ports) { + ofproto_sflow_add_port(os, ofport->odp_port, netdev_get_name(ofport->netdev)); } } @@ -932,8 +929,7 @@ ofproto_destroy(struct ofproto *p) { struct ofservice *ofservice, *next_ofservice; struct ofconn *ofconn, *next_ofconn; - struct ofport *ofport; - unsigned int port_no; + struct ofport *ofport, *next_ofport; size_t i; if (!p) { @@ -951,15 +947,15 @@ 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); - PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) { + HMAP_FOR_EACH_SAFE (ofport, next_ofport, hmap_node, &p->ports) { + hmap_remove(&p->ports, &ofport->hmap_node); ofport_free(ofport); } shash_destroy(&p->port_by_name); @@ -968,8 +964,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); @@ -987,7 +982,7 @@ ofproto_destroy(struct ofproto *p) free(p->serial_desc); free(p->dp_desc); - port_array_destroy(&p->ports); + hmap_destroy(&p->ports); free(p); } @@ -1041,7 +1036,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; @@ -1071,7 +1066,6 @@ ofproto_run1(struct ofproto *p) for (i = 0; i < 50; i++) { struct ofpbuf *buf; - int error; error = dpif_recv(p->dpif, &buf); if (error) { @@ -1105,8 +1099,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); } @@ -1116,13 +1109,12 @@ 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; retval = pvconn_accept(ofservice->pvconn, OFP_VERSION, &vconn); if (!retval) { - struct ofconn *ofconn; struct rconn *rconn; char *name; @@ -1152,19 +1144,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) { @@ -1213,7 +1195,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) { @@ -1236,7 +1218,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++) { @@ -1262,8 +1244,41 @@ ofproto_is_alive(const struct ofproto *p) return !hmap_is_empty(&p->controllers); } +/* Deletes port number 'odp_port' from the datapath for 'ofproto'. + * + * This is almost the same as calling dpif_port_del() directly on the + * datapath, but it also makes 'ofproto' close its open netdev for the port + * (if any). This makes it possible to create a new netdev of a different + * type under the same name, which otherwise the netdev library would refuse + * to do because of the conflict. (The netdev would eventually get closed on + * the next trip through ofproto_run(), but this interface is more direct.) + * + * Returns 0 if successful, otherwise a positive errno. */ +int +ofproto_port_del(struct ofproto *ofproto, uint16_t odp_port) +{ + struct ofport *ofport = get_port(ofproto, odp_port); + const char *name = ofport ? (char *) ofport->opp.name : ""; + int error; + + error = dpif_port_del(ofproto->dpif, odp_port); + if (error) { + VLOG_ERR("%s: failed to remove port %"PRIu16" (%s) interface (%s)", + dpif_name(ofproto->dpif), odp_port, name, strerror(error)); + } else if (ofport) { + /* 'name' is ofport->opp.name and update_port() is going to destroy + * 'ofport'. Just in case update_port() refers to 'name' after it + * destroys 'ofport', make a copy of it around the update_port() + * call. */ + char *devname = xstrdup(name); + update_port(ofproto, devname); + free(devname); + } + return error; +} + int -ofproto_send_packet(struct ofproto *p, const flow_t *flow, +ofproto_send_packet(struct ofproto *p, const struct flow *flow, const union ofp_action *actions, size_t n_actions, const struct ofpbuf *packet) { @@ -1278,27 +1293,26 @@ ofproto_send_packet(struct ofproto *p, const flow_t *flow, /* XXX Should we translate the dpif_execute() errno value into an OpenFlow * error code? */ - dpif_execute(p->dpif, flow->in_port, odp_actions.actions, - odp_actions.n_actions, packet); + dpif_execute(p->dpif, odp_actions.actions, odp_actions.n_actions, packet); return 0; } void -ofproto_add_flow(struct ofproto *p, - const flow_t *flow, uint32_t wildcards, unsigned int priority, +ofproto_add_flow(struct ofproto *p, const struct flow *flow, + uint32_t wildcards, unsigned int priority, const union ofp_action *actions, size_t n_actions, int idle_timeout) { struct rule *rule; rule = rule_create(p, NULL, actions, n_actions, - idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, + idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, 0, 0, false); cls_rule_from_flow(flow, wildcards, priority, &rule->cr); rule_insert(p, rule, NULL, 0); } void -ofproto_delete_flow(struct ofproto *ofproto, const flow_t *flow, +ofproto_delete_flow(struct ofproto *ofproto, const struct flow *flow, uint32_t wildcards, unsigned int priority) { struct rule *rule; @@ -1345,13 +1359,12 @@ reinit_ports(struct ofproto *p) { struct svec devnames; struct ofport *ofport; - unsigned int port_no; struct odp_port *odp_ports; size_t n_odp_ports; size_t i; svec_init(&devnames); - PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) { + 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); @@ -1367,39 +1380,6 @@ reinit_ports(struct ofproto *p) svec_destroy(&devnames); } -static size_t -refresh_port_group(struct ofproto *p, unsigned int group) -{ - uint16_t *ports; - size_t n_ports; - struct ofport *port; - unsigned int port_no; - - assert(group == DP_GROUP_ALL || group == DP_GROUP_FLOOD); - - ports = xmalloc(port_array_count(&p->ports) * sizeof *ports); - n_ports = 0; - PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) { - if (group == DP_GROUP_ALL || !(port->opp.config & OFPPC_NO_FLOOD)) { - ports[n_ports++] = port_no; - } - } - dpif_port_group_set(p->dpif, group, ports, n_ports); - free(ports); - - return n_ports; -} - -static void -refresh_port_groups(struct ofproto *p) -{ - size_t n_flood = refresh_port_group(p, DP_GROUP_FLOOD); - size_t n_all = refresh_port_group(p, DP_GROUP_ALL); - if (p->sflow) { - ofproto_sflow_set_group_sizes(p->sflow, n_flood, n_all); - } -} - static struct ofport * make_ofport(const struct odp_port *odp_port) { @@ -1425,6 +1405,7 @@ make_ofport(const struct odp_port *odp_port) ofport = xmalloc(sizeof *ofport); ofport->netdev = netdev; + ofport->odp_port = odp_port->port; ofport->opp.port_no = odp_port_to_ofp_port(odp_port->port); netdev_get_etheraddr(netdev, ofport->opp.hw_addr); memcpy(ofport->opp.name, odp_port->devname, @@ -1446,7 +1427,7 @@ make_ofport(const struct odp_port *odp_port) static bool ofport_conflicts(const struct ofproto *p, const struct odp_port *odp_port) { - if (port_array_get(&p->ports, odp_port->port)) { + if (get_port(p, odp_port->port)) { VLOG_WARN_RL(&rl, "ignoring duplicate port %"PRIu16" in datapath", odp_port->port); return true; @@ -1483,7 +1464,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; @@ -1497,36 +1478,30 @@ send_port_status(struct ofproto *p, const struct ofport *ofport, hton_ofp_phy_port(&ops->desc); queue_tx(b, ofconn, NULL); } - if (p->ofhooks->port_changed_cb) { - p->ofhooks->port_changed_cb(reason, &ofport->opp, p->aux); - } } static void ofport_install(struct ofproto *p, struct ofport *ofport) { - uint16_t odp_port = ofp_port_to_odp_port(ofport->opp.port_no); const char *netdev_name = (const char *) ofport->opp.name; netdev_monitor_add(p->netdev_monitor, ofport->netdev); - port_array_set(&p->ports, odp_port, ofport); + hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->odp_port, 0)); shash_add(&p->port_by_name, netdev_name, ofport); if (p->sflow) { - ofproto_sflow_add_port(p->sflow, odp_port, netdev_name); + ofproto_sflow_add_port(p->sflow, ofport->odp_port, netdev_name); } } static void ofport_remove(struct ofproto *p, struct ofport *ofport) { - uint16_t odp_port = ofp_port_to_odp_port(ofport->opp.port_no); - netdev_monitor_remove(p->netdev_monitor, ofport->netdev); - port_array_delete(&p->ports, odp_port); + hmap_remove(&p->ports, &ofport->hmap_node); shash_delete(&p->port_by_name, shash_find(&p->port_by_name, (char *) ofport->opp.name)); if (p->sflow) { - ofproto_sflow_del_port(p->sflow, odp_port); + ofproto_sflow_del_port(p->sflow, ofport->odp_port); } } @@ -1539,6 +1514,20 @@ ofport_free(struct ofport *ofport) } } +static struct ofport * +get_port(const struct ofproto *ofproto, uint16_t odp_port) +{ + struct ofport *port; + + HMAP_FOR_EACH_IN_BUCKET (port, hmap_node, + hash_int(odp_port, 0), &ofproto->ports) { + if (port->odp_port == odp_port) { + return port; + } + } + return NULL; +} + static void update_port(struct ofproto *p, const char *devname) { @@ -1566,7 +1555,7 @@ update_port(struct ofproto *p, const char *devname) * reliably but more portably by comparing the old port's MAC * against the new port's MAC. However, this code isn't that smart * and always sends an OFPPR_MODIFY (XXX). */ - old_ofport = port_array_get(&p->ports, odp_port.port); + old_ofport = get_port(p, odp_port.port); } } else if (error != ENOENT && error != ENODEV) { VLOG_WARN_RL(&rl, "dpif_port_query_by_name returned unexpected error " @@ -1606,9 +1595,6 @@ update_port(struct ofproto *p, const char *devname) : !new_ofport ? OFPPR_DELETE : OFPPR_MODIFY)); ofport_free(old_ofport); - - /* Update port groups. */ - refresh_port_groups(p); } static int @@ -1634,7 +1620,6 @@ init_ports(struct ofproto *p) } } free(ports); - refresh_port_groups(p); return 0; } @@ -1836,8 +1821,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; } @@ -1865,8 +1850,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); @@ -1900,7 +1887,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 { @@ -1955,8 +1942,7 @@ execute_odp_actions(struct ofproto *ofproto, uint16_t in_port, } else { int error; - error = dpif_execute(ofproto->dpif, in_port, - actions, n_actions, packet); + error = dpif_execute(ofproto->dpif, actions, n_actions, packet); ofpbuf_delete(packet); return !error; } @@ -1981,7 +1967,7 @@ execute_odp_actions(struct ofproto *ofproto, uint16_t in_port, * Takes ownership of 'packet'. */ static void rule_execute(struct ofproto *ofproto, struct rule *rule, - struct ofpbuf *packet, const flow_t *flow) + struct ofpbuf *packet, const struct flow *flow) { const union odp_action *actions; struct odp_flow_stats stats; @@ -2040,7 +2026,7 @@ rule_insert(struct ofproto *p, struct rule *rule, struct ofpbuf *packet, /* Send the packet and credit it to the rule. */ if (packet) { - flow_t flow; + struct flow flow; flow_extract(packet, 0, in_port, &flow); rule_execute(p, rule, packet, &flow); } @@ -2062,7 +2048,7 @@ rule_insert(struct ofproto *p, struct rule *rule, struct ofpbuf *packet, static struct rule * rule_create_subrule(struct ofproto *ofproto, struct rule *rule, - const flow_t *flow) + const struct flow *flow) { struct rule *subrule = rule_create(ofproto, rule, NULL, 0, rule->idle_timeout, rule->hard_timeout, @@ -2075,6 +2061,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) { @@ -2123,7 +2120,7 @@ do_put_flow(struct ofproto *ofproto, struct rule *rule, int flags, struct odp_flow_put *put) { memset(&put->flow.stats, 0, sizeof put->flow.stats); - put->flow.key = rule->cr.flow; + odp_flow_key_from_flow(&put->flow.key, &rule->cr.flow); put->flow.actions = rule->odp_actions; put->flow.n_actions = rule->n_odp_actions; put->flow.flags = 0; @@ -2212,6 +2209,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) { @@ -2219,7 +2224,7 @@ rule_uninstall(struct ofproto *p, struct rule *rule) if (rule->installed) { struct odp_flow odp_flow; - odp_flow.key = rule->cr.flow; + odp_flow_key_from_flow(&odp_flow.key, &rule->cr.flow); odp_flow.actions = NULL; odp_flow.n_actions = 0; odp_flow.flags = 0; @@ -2341,7 +2346,6 @@ handle_features_request(struct ofproto *p, struct ofconn *ofconn, { struct ofp_switch_features *osf; struct ofpbuf *buf; - unsigned int port_no; struct ofport *port; osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, oh->xid, &buf); @@ -2363,7 +2367,7 @@ handle_features_request(struct ofproto *p, struct ofconn *ofconn, (1u << OFPAT_SET_TP_DST) | (1u << OFPAT_ENQUEUE)); - PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) { + HMAP_FOR_EACH (port, hmap_node, &p->ports) { hton_ofp_phy_port(ofpbuf_put(buf, &port->opp, sizeof port->opp)); } @@ -2426,17 +2430,6 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn, return 0; } -static void -add_output_group_action(struct odp_actions *actions, uint16_t group, - uint16_t *nf_output_iface) -{ - odp_actions_add(actions, ODPAT_OUTPUT_GROUP)->output_group.group = group; - - if (group == DP_GROUP_ALL || group == DP_GROUP_FLOOD) { - *nf_output_iface = NF_OUT_FLOOD; - } -} - static void add_controller_action(struct odp_actions *actions, uint16_t max_len) { @@ -2446,7 +2439,7 @@ add_controller_action(struct odp_actions *actions, uint16_t max_len) struct action_xlate_ctx { /* Input. */ - flow_t flow; /* Flow to which these actions correspond. */ + struct flow flow; /* Flow to which these actions correspond. */ int recurse; /* Recursion level, via xlate_table_action. */ struct ofproto *ofproto; const struct ofpbuf *packet; /* The packet corresponding to 'flow', or a @@ -2471,7 +2464,7 @@ static void do_xlate_actions(const union ofp_action *in, size_t n_in, static void add_output_action(struct action_xlate_ctx *ctx, uint16_t port) { - const struct ofport *ofport = port_array_get(&ctx->ofproto->ports, port); + const struct ofport *ofport = get_port(ctx->ofproto, port); if (ofport) { if (ofport->opp.config & OFPPC_NO_FWD) { @@ -2491,7 +2484,7 @@ add_output_action(struct action_xlate_ctx *ctx, uint16_t port) } static struct rule * -lookup_valid_rule(struct ofproto *ofproto, const flow_t *flow) +lookup_valid_rule(struct ofproto *ofproto, const struct flow *flow) { struct rule *rule; rule = rule_from_cls_rule(classifier_lookup(&ofproto->cls, flow)); @@ -2541,6 +2534,21 @@ xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port) } } +static void +flood_packets(struct ofproto *ofproto, uint16_t odp_in_port, uint32_t mask, + uint16_t *nf_output_iface, struct odp_actions *actions) +{ + struct ofport *ofport; + + HMAP_FOR_EACH (ofport, hmap_node, &ofproto->ports) { + uint16_t odp_port = ofport->odp_port; + if (odp_port != odp_in_port && !(ofport->opp.config & mask)) { + odp_actions_add(actions, ODPAT_OUTPUT)->output.port = odp_port; + } + } + *nf_output_iface = NF_OUT_FLOOD; +} + static void xlate_output_action__(struct action_xlate_ctx *ctx, uint16_t port, uint16_t max_len) @@ -2567,11 +2575,11 @@ xlate_output_action__(struct action_xlate_ctx *ctx, } break; case OFPP_FLOOD: - add_output_group_action(ctx->out, DP_GROUP_FLOOD, - &ctx->nf_output_iface); - break; + flood_packets(ctx->ofproto, ctx->flow.in_port, OFPPC_NO_FLOOD, + &ctx->nf_output_iface, ctx->out); case OFPP_ALL: - add_output_group_action(ctx->out, DP_GROUP_ALL, &ctx->nf_output_iface); + flood_packets(ctx->ofproto, ctx->flow.in_port, 0, + &ctx->nf_output_iface, ctx->out); break; case OFPP_CONTROLLER: add_controller_action(ctx->out, max_len); @@ -2656,12 +2664,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); @@ -2684,6 +2713,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. */ @@ -2701,7 +2739,7 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, const union ofp_action *ia; const struct ofport *port; - port = port_array_get(&ctx->ofproto->ports, ctx->flow.in_port); + port = get_port(ctx->ofproto, ctx->flow.in_port); if (port && port->opp.config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP) && port->opp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp) ? OFPPC_NO_RECV_STP : OFPPC_NO_RECV)) { @@ -2719,13 +2757,16 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, break; case OFPAT_SET_VLAN_VID: - oa = odp_actions_add(ctx->out, ODPAT_SET_VLAN_VID); - ctx->flow.dl_vlan = oa->vlan_vid.vlan_vid = ia->vlan_vid.vlan_vid; + oa = odp_actions_add(ctx->out, ODPAT_SET_DL_TCI); + oa->dl_tci.tci = ia->vlan_vid.vlan_vid & htons(VLAN_VID_MASK); + oa->dl_tci.mask = htons(VLAN_VID_MASK); break; case OFPAT_SET_VLAN_PCP: - oa = odp_actions_add(ctx->out, ODPAT_SET_VLAN_PCP); - ctx->flow.dl_vlan_pcp = oa->vlan_pcp.vlan_pcp = ia->vlan_pcp.vlan_pcp; + oa = odp_actions_add(ctx->out, ODPAT_SET_DL_TCI); + oa->dl_tci.tci = htons((ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) + & VLAN_PCP_MASK); + oa->dl_tci.mask = htons(VLAN_PCP_MASK); break; case OFPAT_STRIP_VLAN: @@ -2792,7 +2833,7 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, static int xlate_actions(const union ofp_action *in, size_t n_in, - const flow_t *flow, struct ofproto *ofproto, + const struct flow *flow, struct ofproto *ofproto, const struct ofpbuf *packet, struct odp_actions *out, tag_type *tags, bool *may_set_up_flow, uint16_t *nf_output_iface) @@ -2862,9 +2903,9 @@ handle_packet_out(struct ofproto *p, struct ofconn *ofconn, struct ofp_packet_out *opo; struct ofpbuf payload, *buffer; struct odp_actions actions; + struct flow flow; int n_actions; uint16_t in_port; - flow_t flow; int error; error = reject_slave_controller(ofconn, oh); @@ -2897,8 +2938,7 @@ handle_packet_out(struct ofproto *p, struct ofconn *ofconn, return error; } - dpif_execute(p->dpif, flow.in_port, actions.actions, actions.n_actions, - &payload); + dpif_execute(p->dpif, actions.actions, actions.n_actions, &payload); ofpbuf_delete(buffer); return 0; @@ -2916,17 +2956,14 @@ update_port_config(struct ofproto *p, struct ofport *port, netdev_turn_flags_on(port->netdev, NETDEV_UP, true); } } -#define REVALIDATE_BITS (OFPPC_NO_RECV | OFPPC_NO_RECV_STP | OFPPC_NO_FWD) +#define REVALIDATE_BITS (OFPPC_NO_RECV | OFPPC_NO_RECV_STP | \ + OFPPC_NO_FWD | OFPPC_NO_FLOOD) if (mask & REVALIDATE_BITS) { COVERAGE_INC(ofproto_costly_flags); port->opp.config ^= mask & REVALIDATE_BITS; p->need_revalidate = true; } #undef REVALIDATE_BITS - if (mask & OFPPC_NO_FLOOD) { - port->opp.config ^= OFPPC_NO_FLOOD; - refresh_port_groups(p); - } if (mask & OFPPC_NO_PACKET_IN) { port->opp.config ^= OFPPC_NO_PACKET_IN; } @@ -2950,8 +2987,7 @@ handle_port_mod(struct ofproto *p, struct ofconn *ofconn, } opm = (struct ofp_port_mod *) oh; - port = port_array_get(&p->ports, - ofp_port_to_odp_port(ntohs(opm->port_no))); + port = get_port(p, ofp_port_to_odp_port(ntohs(opm->port_no))); if (!port) { return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT); } else if (memcmp(port->opp.hw_addr, opm->hw_addr, OFP_ETH_ALEN)) { @@ -3018,17 +3054,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) @@ -3037,12 +3062,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); @@ -3076,19 +3106,19 @@ handle_table_stats_request(struct ofproto *p, struct ofconn *ofconn, } static void -append_port_stat(struct ofport *port, uint16_t port_no, struct ofconn *ofconn, +append_port_stat(struct ofport *port, struct ofconn *ofconn, struct ofpbuf **msgp) { struct netdev_stats stats; struct ofp_port_stats *ops; - /* Intentionally ignore return value, since errors will set - * 'stats' to all-1s, which is correct for OpenFlow, and + /* Intentionally ignore return value, since errors will set + * 'stats' to all-1s, which is correct for OpenFlow, and * netdev_get_stats() will log errors. */ netdev_get_stats(port->netdev, &stats); ops = append_stats_reply(sizeof *ops, ofconn, msgp); - ops->port_no = htons(odp_port_to_ofp_port(port_no)); + ops->port_no = htons(port->opp.port_no); memset(ops->pad, 0, sizeof ops->pad); ops->rx_packets = htonll(stats.rx_packets); ops->tx_packets = htonll(stats.tx_packets); @@ -3113,7 +3143,6 @@ handle_port_stats_request(struct ofproto *p, struct ofconn *ofconn, struct ofp_port_stats *ops; struct ofpbuf *msg; struct ofport *port; - unsigned int port_no; if (arg_size != sizeof *psr) { return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); @@ -3122,14 +3151,13 @@ handle_port_stats_request(struct ofproto *p, struct ofconn *ofconn, msg = start_stats_reply(osr, sizeof *ops * 16); if (psr->port_no != htons(OFPP_NONE)) { - port = port_array_get(&p->ports, - ofp_port_to_odp_port(ntohs(psr->port_no))); + port = get_port(p, ofp_port_to_odp_port(ntohs(psr->port_no))); if (port) { - append_port_stat(port, ntohs(psr->port_no), ofconn, &msg); + append_port_stat(port, ofconn, &msg); } } else { - PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) { - append_port_stat(port, port_no, ofconn, &msg); + HMAP_FOR_EACH (port, hmap_node, &p->ports) { + append_port_stat(port, ofconn, &msg); } } @@ -3173,13 +3201,13 @@ 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) { - odp_flows[i++].key = subrule->cr.flow; + LIST_FOR_EACH (subrule, list, &rule->list) { + odp_flow_key_from_flow(&odp_flows[i++].key, &subrule->cr.flow); packet_count += subrule->packet_count; byte_count += subrule->byte_count; } } else { - odp_flows[0].key = rule->cr.flow; + odp_flow_key_from_flow(&odp_flows[0].key, &rule->cr.flow); } /* Fetch up-to-date statistics from the datapath and add them in. */ @@ -3234,7 +3262,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 @@ -3303,11 +3333,13 @@ 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"); } -/* Adds a pretty-printed description of all flows to 'results', including +/* Adds a pretty-printed description of all flows to 'results', including * those marked hidden by secchan (e.g., by in-band control). */ void ofproto_get_all_flows(struct ofproto *p, struct ds *results) @@ -3391,8 +3423,8 @@ handle_aggregate_stats_request(struct ofproto *p, struct ofconn *ofconn, struct queue_stats_cbdata { struct ofconn *ofconn; + struct ofport *ofport; struct ofpbuf *msg; - uint16_t port_no; }; static void @@ -3402,7 +3434,7 @@ put_queue_stats(struct queue_stats_cbdata *cbdata, uint32_t queue_id, struct ofp_queue_stats *reply; reply = append_stats_reply(sizeof *reply, cbdata->ofconn, &cbdata->msg); - reply->port_no = htons(cbdata->port_no); + reply->port_no = htons(cbdata->ofport->opp.port_no); memset(reply->pad, 0, sizeof reply->pad); reply->queue_id = htonl(queue_id); reply->tx_bytes = htonll(stats->tx_bytes); @@ -3421,19 +3453,19 @@ handle_queue_stats_dump_cb(uint32_t queue_id, } static void -handle_queue_stats_for_port(struct ofport *port, uint16_t port_no, - uint32_t queue_id, +handle_queue_stats_for_port(struct ofport *port, uint32_t queue_id, struct queue_stats_cbdata *cbdata) { - cbdata->port_no = port_no; + cbdata->ofport = port; if (queue_id == OFPQ_ALL) { netdev_dump_queue_stats(port->netdev, handle_queue_stats_dump_cb, cbdata); } 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); + } } } @@ -3461,13 +3493,13 @@ 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) { - PORT_ARRAY_FOR_EACH (port, &ofproto->ports, port_no) { - handle_queue_stats_for_port(port, port_no, queue_id, &cbdata); + HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) { + handle_queue_stats_for_port(port, queue_id, &cbdata); } } else if (port_no < ofproto->max_ports) { - port = port_array_get(&ofproto->ports, port_no); + port = get_port(ofproto, ofp_port_to_odp_port(port_no)); if (port) { - handle_queue_stats_for_port(port, port_no, queue_id, &cbdata); + handle_queue_stats_for_port(port, queue_id, &cbdata); } } else { ofpbuf_delete(cbdata.msg); @@ -3571,7 +3603,7 @@ add_flow(struct ofproto *p, struct ofconn *ofconn, int error; if (ofm->flags & htons(OFPFF_CHECK_OVERLAP)) { - flow_t flow; + struct flow flow; uint32_t wildcards; flow_from_match(&ofm->match, p->tun_id_from_cookie, ofm->cookie, @@ -3606,7 +3638,7 @@ static struct rule * find_flow_strict(struct ofproto *p, const struct ofp_flow_mod *ofm) { uint32_t wildcards; - flow_t flow; + struct flow flow; flow_from_match(&ofm->match, p->tun_id_from_cookie, ofm->cookie, &flow, &wildcards); @@ -3621,7 +3653,7 @@ send_buffered_packet(struct ofproto *ofproto, struct ofconn *ofconn, { struct ofpbuf *packet; uint16_t in_port; - flow_t flow; + struct flow flow; int error; if (ofm->buffer_id == htonl(UINT32_MAX)) { @@ -3731,14 +3763,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. */ @@ -3948,8 +3980,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; } @@ -4092,7 +4123,7 @@ handle_odp_miss_msg(struct ofproto *p, struct ofpbuf *packet) struct odp_msg *msg = packet->data; struct rule *rule; struct ofpbuf payload; - flow_t flow; + struct flow flow; payload.data = msg + 1; payload.size = msg->length - sizeof *msg; @@ -4106,13 +4137,13 @@ handle_odp_miss_msg(struct ofproto *p, struct ofpbuf *packet) memset(&action, 0, sizeof(action)); action.output.type = ODPAT_OUTPUT; action.output.port = ODPP_LOCAL; - dpif_execute(p->dpif, flow.in_port, &action, 1, &payload); + dpif_execute(p->dpif, &action, 1, &payload); } rule = lookup_valid_rule(p, &flow); if (!rule) { /* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */ - struct ofport *port = port_array_get(&p->ports, msg->port); + struct ofport *port = get_port(p, msg->port); if (port) { if (port->opp.config & OFPPC_NO_PACKET_IN) { COVERAGE_INC(ofproto_no_packet_in); @@ -4154,7 +4185,8 @@ handle_odp_miss_msg(struct ofproto *p, struct ofpbuf *packet) * * See the top-level comment in fail-open.c for more information. */ - send_packet_in(p, ofpbuf_clone(packet)); + send_packet_in(p, ofpbuf_clone_with_headroom(packet, + DPIF_RECV_MSG_PADDING)); } ofpbuf_pull(packet, sizeof *msg); @@ -4191,6 +4223,288 @@ 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; + struct flow flow; + + odp_flow_key_to_flow(&f->key, &flow); + + rule = rule_from_cls_rule( + classifier_find_rule_exactly(&p->cls, &flow, 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_from_flow(&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_) { @@ -4207,7 +4521,7 @@ revalidate_cb(struct cls_rule *sub_, void *cbdata_) static bool revalidate_rule(struct ofproto *p, struct rule *rule) { - const flow_t *flow = &rule->cr.flow; + const struct flow *flow = &rule->cr.flow; COVERAGE_INC(ofproto_revalidate_rule); if (rule->super) { @@ -4257,19 +4571,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) @@ -4278,6 +4579,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 @@ -4285,8 +4590,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); @@ -4301,122 +4606,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_) @@ -4548,7 +4737,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); @@ -4568,7 +4757,7 @@ pick_datapath_id(const struct ofproto *ofproto) { const struct ofport *port; - port = port_array_get(&ofproto->ports, ODPP_LOCAL); + port = get_port(ofproto, ODPP_LOCAL); if (port) { uint8_t ea[ETH_ADDR_LEN]; int error; @@ -4592,7 +4781,7 @@ pick_fallback_dpid(void) } static bool -default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet, +default_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet, struct odp_actions *actions, tag_type *tags, uint16_t *nf_output_iface, void *ofproto_) { @@ -4623,7 +4812,8 @@ default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet, out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags, NULL); if (out_port < 0) { - add_output_group_action(actions, DP_GROUP_FLOOD, nf_output_iface); + flood_packets(ofproto, flow->in_port, OFPPC_NO_FLOOD, + nf_output_iface, actions); } else if (out_port != flow->in_port) { odp_actions_add(actions, ODPAT_OUTPUT)->output.port = out_port; *nf_output_iface = out_port; @@ -4635,7 +4825,6 @@ default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet, } static const struct ofhooks default_ofhooks = { - NULL, default_normal_ofhook_cb, NULL, NULL