X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=6de30b399eb54afe2f5e52e77f9099919f5af3cb;hb=4a4cdb3b24f582e6a751a7a67bf4cbdbd071fcf6;hp=844083d8bf3a6ea07c60ee400499efe1d4750f3b;hpb=c9b5816cfd8fccc2abedd258d914882033bfcf0a;p=openvswitch diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 844083d8..6de30b39 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,9 +69,12 @@ 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 *); @@ -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 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_); @@ -336,6 +338,7 @@ static void handle_openflow(struct ofconn *, struct ofproto *, 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 +391,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 +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; @@ -553,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) { @@ -612,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; } @@ -661,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)); @@ -678,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, @@ -718,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); } } @@ -883,12 +884,11 @@ 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 +932,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 +950,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 +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); @@ -987,7 +985,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 +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; @@ -1071,7 +1069,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 +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); } @@ -1116,13 +1112,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; @@ -1153,18 +1148,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) { @@ -1213,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) { @@ -1236,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++) { @@ -1345,13 +1330,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); @@ -1373,15 +1357,14 @@ 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); + ports = xmalloc(hmap_count(&p->ports) * sizeof *ports); n_ports = 0; - PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) { + HMAP_FOR_EACH (port, hmap_node, &p->ports) { if (group == DP_GROUP_ALL || !(port->opp.config & OFPPC_NO_FLOOD)) { - ports[n_ports++] = port_no; + ports[n_ports++] = port->odp_port; } } dpif_port_group_set(p->dpif, group, ports, n_ports); @@ -1425,6 +1408,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 +1430,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 +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; @@ -1505,28 +1489,25 @@ send_port_status(struct ofproto *p, const struct ofport *ofport, 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 +1520,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 +1561,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 " @@ -1836,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; } @@ -1865,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); @@ -1900,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 { @@ -2341,7 +2338,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 +2359,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)); } @@ -2471,7 +2467,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) { @@ -2656,12 +2652,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 +2701,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 +2727,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)) { @@ -2950,8 +2976,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)) { @@ -3076,7 +3101,7 @@ 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; @@ -3088,7 +3113,7 @@ append_port_stat(struct ofport *port, uint16_t port_no, struct ofconn *ofconn, 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 +3138,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 +3146,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,7 +3196,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; @@ -3234,7 +3257,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,7 +3328,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"); } @@ -3391,8 +3418,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 +3429,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 +3448,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 +3488,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); @@ -3731,14 +3758,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 +3975,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; } @@ -4112,7 +4138,7 @@ handle_odp_miss_msg(struct ofproto *p, struct ofpbuf *packet) 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); @@ -4192,6 +4218,180 @@ 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, 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 +4458,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,7 +4473,7 @@ 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) { + LIST_FOR_EACH (ofconn, node, &p->all_conns) { if (rule->send_flow_removed && rconn_is_connected(ofconn->rconn) && ofconn_receives_async_msgs(ofconn)) { if (prev) { @@ -4302,122 +4489,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_) @@ -4549,7 +4620,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); @@ -4569,7 +4640,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;