X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=ofproto%2Fofproto-dpif.c;h=1d964ddc8a89f13717507fcde0033dcf69a79cc7;hb=6bef3c7ca859f208239ca61ec3b25c09a3571553;hp=ab567285fa292efb54d552850e754728550a3706;hpb=1e684d7d7f533dae8829957e54edc0ee727c5155;p=cascardo%2Fovs.git diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index ab567285f..1d964ddc8 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -37,6 +37,7 @@ #include "lacp.h" #include "learn.h" #include "mac-learning.h" +#include "mcast-snooping.h" #include "meta-flow.h" #include "multipath.h" #include "netdev-vport.h" @@ -73,9 +74,6 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif); COVERAGE_DEFINE(ofproto_dpif_expired); COVERAGE_DEFINE(packet_in_overflow); -/* No bfd/cfm status change. */ -#define NO_STATUS_CHANGE -1 - struct flow_miss; struct rule_dpif { @@ -87,6 +85,12 @@ struct rule_dpif { * recently been processed by a revalidator. */ struct ovs_mutex stats_mutex; struct dpif_flow_stats stats OVS_GUARDED; + + /* If non-zero then the recirculation id that has + * been allocated for use with this rule. + * The recirculation id and associated internal flow should + * be freed when the rule is freed */ + uint32_t recirc_id; }; /* RULE_CAST() depends on this. */ @@ -141,6 +145,10 @@ static void stp_wait(struct ofproto_dpif *ofproto); static int set_stp_port(struct ofport *, const struct ofproto_port_stp_settings *); +static void rstp_run(struct ofproto_dpif *ofproto); +static void set_rstp_port(struct ofport *, + const struct ofproto_port_rstp_settings *); + struct ofport_dpif { struct hmap_node odp_port_node; /* In dpif_backer's "odp_to_ofport_map". */ struct ofport up; @@ -161,6 +169,10 @@ struct ofport_dpif { enum stp_state stp_state; /* Always STP_DISABLED if STP not in use. */ long long int stp_state_entered; + /* Rapid Spanning Tree. */ + struct rstp_port *rstp_port; /* Rapid Spanning Tree Protocol, if any. */ + enum rstp_state rstp_state; /* Always RSTP_DISABLED if RSTP not in use. */ + /* Queue to DSCP mapping. */ struct ofproto_port_queue *qdscp; size_t n_qdscp; @@ -209,11 +221,6 @@ static int set_bfd(struct ofport *, const struct smap *); static int set_cfm(struct ofport *, const struct cfm_settings *); static void ofport_update_peer(struct ofport_dpif *); -struct dpif_completion { - struct list list_node; - struct ofoperation *op; -}; - /* Reasons that we might need to revalidate every datapath flow, and * corresponding coverage counters. * @@ -225,17 +232,21 @@ struct dpif_completion { enum revalidate_reason { REV_RECONFIGURE = 1, /* Switch configuration changed. */ REV_STP, /* Spanning tree protocol port status change. */ + REV_RSTP, /* RSTP port status change. */ REV_BOND, /* Bonding changed. */ REV_PORT_TOGGLED, /* Port enabled or disabled by CFM, LACP, ...*/ REV_FLOW_TABLE, /* Flow table changed. */ REV_MAC_LEARNING, /* Mac learning changed. */ + REV_MCAST_SNOOPING, /* Multicast snooping changed. */ }; COVERAGE_DEFINE(rev_reconfigure); COVERAGE_DEFINE(rev_stp); +COVERAGE_DEFINE(rev_rstp); COVERAGE_DEFINE(rev_bond); COVERAGE_DEFINE(rev_port_toggled); COVERAGE_DEFINE(rev_flow_table); COVERAGE_DEFINE(rev_mac_learning); +COVERAGE_DEFINE(rev_mcast_snooping); /* All datapaths of a given type share a single dpif backer instance. */ struct dpif_backer { @@ -262,6 +273,10 @@ struct dpif_backer { * False if the datapath supports only 8-byte (or shorter) userdata. */ bool variable_length_userdata; + /* True if the datapath supports masked data in OVS_ACTION_ATTR_SET + * actions. */ + bool masked_set_action; + /* Maximum number of MPLS label stack entries that the datapath supports * in a match */ size_t max_mpls_depth; @@ -288,6 +303,7 @@ struct ofproto_dpif { struct dpif_ipfix *ipfix; struct hmap bundles; /* Contains "struct ofbundle"s. */ struct mac_learning *ml; + struct mcast_snooping *ms; bool has_bonded_bundles; bool lacp_enabled; struct mbridge *mbridge; @@ -300,6 +316,10 @@ struct ofproto_dpif { struct stp *stp; long long int stp_last_tick; + /* Rapid Spanning Tree. */ + struct rstp *rstp; + long long int rstp_last_tick; + /* VLAN splinters. */ struct ovs_mutex vsp_mutex; struct hmap realdev_vid_map OVS_GUARDED; /* (realdev,vid) -> vlandev. */ @@ -364,18 +384,6 @@ ofproto_dpif_flow_mod(struct ofproto_dpif *ofproto, ofproto_flow_mod(&ofproto->up, fm); } -/* Resets the modified time for 'rule' or an equivalent rule. If 'rule' is not - * in the classifier, but an equivalent rule is, unref 'rule' and ref the new - * rule. Otherwise if 'rule' is no longer installed in the classifier, - * reinstall it. - * - * Returns the rule whose modified time has been reset. */ -struct rule_dpif * -ofproto_dpif_refresh_rule(struct rule_dpif *rule) -{ - return rule_dpif_cast(ofproto_refresh_rule(&rule->up)); -} - /* Appends 'pin' to the queue of "packet ins" to be sent to the controller. * Takes ownership of 'pin' and pin->packet. */ void @@ -501,6 +509,7 @@ type_run(const char *type) } dpif_run(backer->dpif); + udpif_run(backer->udpif); /* If vswitchd started with other_config:flow_restore_wait set as "true", * and the configuration has now changed to "false", enable receiving @@ -523,6 +532,8 @@ type_run(const char *type) udpif_set_threads(backer->udpif, n_handlers, n_revalidators); } + dpif_poll_threads_set(backer->dpif, n_dpdk_rxqs, pmd_cpu_mask); + if (backer->need_revalidate) { struct ofproto_dpif *ofproto; struct simap_node *node; @@ -584,10 +595,12 @@ type_run(const char *type) switch (backer->need_revalidate) { case REV_RECONFIGURE: COVERAGE_INC(rev_reconfigure); break; case REV_STP: COVERAGE_INC(rev_stp); break; + case REV_RSTP: COVERAGE_INC(rev_rstp); break; case REV_BOND: COVERAGE_INC(rev_bond); break; case REV_PORT_TOGGLED: COVERAGE_INC(rev_port_toggled); break; case REV_FLOW_TABLE: COVERAGE_INC(rev_flow_table); break; case REV_MAC_LEARNING: COVERAGE_INC(rev_mac_learning); break; + case REV_MCAST_SNOOPING: COVERAGE_INC(rev_mcast_snooping); break; } backer->need_revalidate = 0; @@ -599,18 +612,19 @@ type_run(const char *type) continue; } - ovs_rwlock_wrlock(&xlate_rwlock); + xlate_txn_start(); xlate_ofproto_set(ofproto, ofproto->up.name, ofproto->backer->dpif, ofproto->miss_rule, ofproto->no_packet_in_rule, ofproto->ml, - ofproto->stp, ofproto->mbridge, - ofproto->sflow, ofproto->ipfix, + ofproto->stp, ofproto->rstp, ofproto->ms, + ofproto->mbridge, ofproto->sflow, ofproto->ipfix, ofproto->netflow, ofproto->up.frag_handling, ofproto->up.forward_bpdu, connmgr_has_in_band(ofproto->up.connmgr), ofproto->backer->enable_recirc, ofproto->backer->variable_length_userdata, - ofproto->backer->max_mpls_depth); + ofproto->backer->max_mpls_depth, + ofproto->backer->masked_set_action); HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { xlate_bundle_set(ofproto, bundle, bundle->name, @@ -628,11 +642,12 @@ type_run(const char *type) ofport->up.ofp_port, ofport->odp_port, ofport->up.netdev, ofport->cfm, ofport->bfd, ofport->peer, stp_port, - ofport->qdscp, ofport->n_qdscp, - ofport->up.pp.config, ofport->up.pp.state, - ofport->is_tunnel, ofport->may_enable); + ofport->rstp_port, ofport->qdscp, + ofport->n_qdscp, ofport->up.pp.config, + ofport->up.pp.state, ofport->is_tunnel, + ofport->may_enable); } - ovs_rwlock_unlock(&xlate_rwlock); + xlate_txn_commit(); } udpif_revalidate(backer->udpif); @@ -835,6 +850,7 @@ struct odp_garbage { static bool check_variable_length_userdata(struct dpif_backer *backer); static size_t check_max_mpls_depth(struct dpif_backer *backer); static bool check_recirc(struct dpif_backer *backer); +static bool check_masked_set_action(struct dpif_backer *backer); static int open_dpif_backer(const char *type, struct dpif_backer **backerp) @@ -928,6 +944,11 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp) shash_add(&all_dpif_backers, type, backer); + backer->enable_recirc = check_recirc(backer); + backer->max_mpls_depth = check_max_mpls_depth(backer); + backer->masked_set_action = check_masked_set_action(backer); + backer->rid_pool = recirc_id_pool_create(); + error = dpif_recv_set(backer->dpif, backer->recv_set_enable); if (error) { VLOG_ERR("failed to listen on datapath of type %s: %s", @@ -935,15 +956,16 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp) close_dpif_backer(backer); return error; } - backer->enable_recirc = check_recirc(backer); - backer->variable_length_userdata = check_variable_length_userdata(backer); - backer->max_mpls_depth = check_max_mpls_depth(backer); - backer->rid_pool = recirc_id_pool_create(); if (backer->recv_set_enable) { udpif_set_threads(backer->udpif, n_handlers, n_revalidators); } + /* This check fails if performed before udpif threads have been set, + * as the kernel module checks that the 'pid' in userspace action + * is non-zero. */ + backer->variable_length_userdata = check_variable_length_userdata(backer); + return error; } @@ -970,7 +992,7 @@ check_recirc(struct dpif_backer *backer) ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&key, &flow, NULL, 0, true); - error = dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY, + error = dpif_flow_put(backer->dpif, DPIF_FP_CREATE, ofpbuf_data(&key), ofpbuf_size(&key), NULL, 0, NULL, 0, NULL); if (error && error != EEXIST) { @@ -1054,11 +1076,6 @@ check_variable_length_userdata(struct dpif_backer *backer) switch (error) { case 0: - /* Variable-length userdata is supported. - * - * Purge received packets to avoid processing the nonsense packet we - * sent to userspace, then report success. */ - dpif_recv_purge(backer->dpif); return true; case ERANGE: @@ -1103,8 +1120,9 @@ check_max_mpls_depth(struct dpif_backer *backer) ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&key, &flow, NULL, 0, false); - error = dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY, - ofpbuf_data(&key), ofpbuf_size(&key), NULL, 0, NULL, 0, NULL); + error = dpif_flow_put(backer->dpif, DPIF_FP_CREATE, + ofpbuf_data(&key), ofpbuf_size(&key), NULL, 0, + NULL, 0, NULL); if (error && error != EEXIST) { if (error != EINVAL) { VLOG_WARN("%s: MPLS stack length feature probe failed (%s)", @@ -1113,7 +1131,8 @@ check_max_mpls_depth(struct dpif_backer *backer) break; } - error = dpif_flow_del(backer->dpif, ofpbuf_data(&key), ofpbuf_size(&key), NULL); + error = dpif_flow_del(backer->dpif, ofpbuf_data(&key), + ofpbuf_size(&key), NULL); if (error) { VLOG_WARN("%s: failed to delete MPLS feature probe flow", dpif_name(backer->dpif)); @@ -1125,6 +1144,55 @@ check_max_mpls_depth(struct dpif_backer *backer) return n; } +/* Tests whether 'backer''s datapath supports masked data in + * OVS_ACTION_ATTR_SET actions. We need to disable some features on older + * datapaths that don't support this feature. */ +static bool +check_masked_set_action(struct dpif_backer *backer) +{ + struct eth_header *eth; + struct ofpbuf actions; + struct dpif_execute execute; + struct ofpbuf packet; + int error; + struct ovs_key_ethernet key, mask; + + /* Compose a set action that will cause an EINVAL error on older + * datapaths that don't support masked set actions. + * Avoid using a full mask, as it could be translated to a non-masked + * set action instead. */ + ofpbuf_init(&actions, 64); + memset(&key, 0x53, sizeof key); + memset(&mask, 0x7f, sizeof mask); + commit_masked_set_action(&actions, OVS_KEY_ATTR_ETHERNET, &key, &mask, + sizeof key); + + /* Compose a dummy ethernet packet. */ + ofpbuf_init(&packet, ETH_HEADER_LEN); + eth = ofpbuf_put_zeros(&packet, ETH_HEADER_LEN); + eth->eth_type = htons(0x1234); + + /* Execute the actions. On older datapaths this fails with EINVAL, on + * newer datapaths it succeeds. */ + execute.actions = ofpbuf_data(&actions); + execute.actions_len = ofpbuf_size(&actions); + execute.packet = &packet; + execute.md = PKT_METADATA_INITIALIZER(0); + execute.needs_help = false; + + error = dpif_execute(backer->dpif, &execute); + + ofpbuf_uninit(&packet); + ofpbuf_uninit(&actions); + + if (error) { + /* Masked set action is not supported. */ + VLOG_INFO("%s: datapath does not support masked set action feature.", + dpif_name(backer->dpif)); + } + return !error; +} + static int construct(struct ofproto *ofproto_) { @@ -1141,9 +1209,11 @@ construct(struct ofproto *ofproto_) ofproto->sflow = NULL; ofproto->ipfix = NULL; ofproto->stp = NULL; + ofproto->rstp = NULL; ofproto->dump_seq = 0; hmap_init(&ofproto->bundles); ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME); + ofproto->ms = NULL; ofproto->mbridge = mbridge_create(); ofproto->has_bonded_bundles = false; ofproto->lacp_enabled = false; @@ -1205,7 +1275,8 @@ add_internal_miss_flow(struct ofproto_dpif *ofproto, int id, match_init_catchall(&match); match_set_reg(&match, 0, id); - error = ofproto_dpif_add_internal_flow(ofproto, &match, 0, ofpacts, &rule); + error = ofproto_dpif_add_internal_flow(ofproto, &match, 0, 0, ofpacts, + &rule); *rulep = error ? NULL : rule_dpif_cast(rule); return error; @@ -1256,14 +1327,13 @@ add_internal_flows(struct ofproto_dpif *ofproto) * (priority=2), recirc=0, actions=resubmit(, 0) */ resubmit = ofpact_put_RESUBMIT(&ofpacts); - resubmit->ofpact.compat = 0; resubmit->in_port = OFPP_IN_PORT; resubmit->table_id = 0; match_init_catchall(&match); match_set_recirc_id(&match, 0); - error = ofproto_dpif_add_internal_flow(ofproto, &match, 2, &ofpacts, + error = ofproto_dpif_add_internal_flow(ofproto, &match, 2, 0, &ofpacts, &unused_rulep); if (error) { return error; @@ -1276,7 +1346,7 @@ add_internal_flows(struct ofproto_dpif *ofproto) */ ofpbuf_clear(&ofpacts); match_init_catchall(&match); - error = ofproto_dpif_add_internal_flow(ofproto, &match, 1, &ofpacts, + error = ofproto_dpif_add_internal_flow(ofproto, &match, 1, 0, &ofpacts, &unused_rulep); return error; @@ -1286,15 +1356,15 @@ static void destruct(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - struct rule_dpif *rule, *next_rule; struct ofproto_packet_in *pin, *next_pin; + struct rule_dpif *rule; struct oftable *table; struct list pins; ofproto->backer->need_revalidate = REV_RECONFIGURE; - ovs_rwlock_wrlock(&xlate_rwlock); + xlate_txn_start(); xlate_remove_ofproto(ofproto); - ovs_rwlock_unlock(&xlate_rwlock); + xlate_txn_commit(); /* Ensure that the upcall processing threads have no remaining references * to the ofproto or anything in it. */ @@ -1303,12 +1373,7 @@ destruct(struct ofproto *ofproto_) hmap_remove(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node); OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) { - struct cls_cursor cursor; - - fat_rwlock_rdlock(&table->cls.rwlock); - cls_cursor_init(&cursor, &table->cls, NULL); - fat_rwlock_unlock(&table->cls.rwlock); - CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) { + CLS_FOR_EACH_SAFE (rule, up.cr, &table->cls) { ofproto_rule_delete(&ofproto->up, &rule->up); } } @@ -1325,8 +1390,10 @@ destruct(struct ofproto *ofproto_) netflow_unref(ofproto->netflow); dpif_sflow_unref(ofproto->sflow); + dpif_ipfix_unref(ofproto->ipfix); hmap_destroy(&ofproto->bundles); mac_learning_unref(ofproto->ml); + mcast_snooping_unref(ofproto->ms); hmap_destroy(&ofproto->vlandev_map); hmap_destroy(&ofproto->realdev_vid_map); @@ -1354,6 +1421,7 @@ run(struct ofproto *ofproto_) ovs_rwlock_wrlock(&ofproto->ml->rwlock); mac_learning_flush(ofproto->ml); ovs_rwlock_unlock(&ofproto->ml->rwlock); + mcast_snooping_mdb_flush(ofproto->ms); } /* Always updates the ofproto->pins_seqno to avoid frequent wakeup during @@ -1406,12 +1474,17 @@ run(struct ofproto *ofproto_) } stp_run(ofproto); + rstp_run(ofproto); ovs_rwlock_wrlock(&ofproto->ml->rwlock); if (mac_learning_run(ofproto->ml)) { ofproto->backer->need_revalidate = REV_MAC_LEARNING; } ovs_rwlock_unlock(&ofproto->ml->rwlock); + if (mcast_snooping_run(ofproto->ms)) { + ofproto->backer->need_revalidate = REV_MCAST_SNOOPING; + } + new_dump_seq = seq_read(udpif_dump_seq(ofproto->backer->udpif)); if (ofproto->dump_seq != new_dump_seq) { struct rule *rule, *next_rule; @@ -1473,6 +1546,7 @@ wait(struct ofproto *ofproto_) ovs_rwlock_rdlock(&ofproto->ml->rwlock); mac_learning_wait(ofproto->ml); ovs_rwlock_unlock(&ofproto->ml->rwlock); + mcast_snooping_wait(ofproto->ms); stp_wait(ofproto); if (ofproto->backer->need_revalidate) { /* Shouldn't happen, but if it does just go around again. */ @@ -1507,44 +1581,25 @@ flush(struct ofproto *ofproto_) } static void -get_features(struct ofproto *ofproto_ OVS_UNUSED, - bool *arp_match_ip, enum ofputil_action_bitmap *actions) -{ - *arp_match_ip = true; - *actions = (OFPUTIL_A_OUTPUT | - OFPUTIL_A_SET_VLAN_VID | - OFPUTIL_A_SET_VLAN_PCP | - OFPUTIL_A_STRIP_VLAN | - OFPUTIL_A_SET_DL_SRC | - OFPUTIL_A_SET_DL_DST | - OFPUTIL_A_SET_NW_SRC | - OFPUTIL_A_SET_NW_DST | - OFPUTIL_A_SET_NW_TOS | - OFPUTIL_A_SET_TP_SRC | - OFPUTIL_A_SET_TP_DST | - OFPUTIL_A_ENQUEUE); -} - -static void -get_tables(struct ofproto *ofproto_, struct ofp12_table_stats *ots) +query_tables(struct ofproto *ofproto, + struct ofputil_table_features *features, + struct ofputil_table_stats *stats) { - struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - struct dpif_dp_stats s; - uint64_t n_miss, n_no_pkt_in, n_bytes, n_dropped_frags; - uint64_t n_lookup; - long long int used; + strcpy(features->name, "classifier"); - strcpy(ots->name, "classifier"); + if (stats) { + int i; - dpif_get_dp_stats(ofproto->backer->dpif, &s); - rule_get_stats(&ofproto->miss_rule->up, &n_miss, &n_bytes, &used); - rule_get_stats(&ofproto->no_packet_in_rule->up, &n_no_pkt_in, &n_bytes, - &used); - rule_get_stats(&ofproto->drop_frags_rule->up, &n_dropped_frags, &n_bytes, - &used); - n_lookup = s.n_hit + s.n_missed - n_dropped_frags; - ots->lookup_count = htonll(n_lookup); - ots->matched_count = htonll(n_lookup - n_miss - n_no_pkt_in); + for (i = 0; i < ofproto->n_tables; i++) { + unsigned long missed, matched; + + atomic_read_relaxed(&ofproto->tables[i].n_matched, &matched); + atomic_read_relaxed(&ofproto->tables[i].n_missed, &missed); + + stats[i].matched_count = matched; + stats[i].lookup_count = matched + missed; + } + } } static struct ofport * @@ -1575,9 +1630,11 @@ port_construct(struct ofport *port_) port->bundle = NULL; port->cfm = NULL; port->bfd = NULL; - port->may_enable = true; + port->may_enable = false; port->stp_port = NULL; port->stp_state = STP_DISABLED; + port->rstp_port = NULL; + port->rstp_state = RSTP_DISABLED; port->is_tunnel = false; port->peer = NULL; port->qdscp = NULL; @@ -1611,6 +1668,9 @@ port_construct(struct ofport *port_) if (netdev_get_tunnel_config(netdev)) { tnl_port_add(port, port->up.netdev, port->odp_port); port->is_tunnel = true; + if (ofproto->ipfix) { + dpif_ipfix_add_tunnel_port(ofproto->ipfix, port_, port->odp_port); + } } else { /* Sanity-check that a mapping doesn't already exist. This * shouldn't happen for non-tunnel ports. */ @@ -1645,9 +1705,9 @@ port_destruct(struct ofport *port_) const char *dp_port_name; ofproto->backer->need_revalidate = REV_RECONFIGURE; - ovs_rwlock_wrlock(&xlate_rwlock); + xlate_txn_start(); xlate_ofport_remove(port); - ovs_rwlock_unlock(&xlate_rwlock); + xlate_txn_commit(); dp_port_name = netdev_vport_get_dpif_port(port->up.netdev, namebuf, sizeof namebuf); @@ -1672,6 +1732,10 @@ port_destruct(struct ofport *port_) ovs_rwlock_unlock(&ofproto->backer->odp_to_ofport_lock); } + if (port->is_tunnel && ofproto->ipfix) { + dpif_ipfix_del_tunnel_port(ofproto->ipfix, port->odp_port); + } + tnl_port_del(port); sset_find_and_delete(&ofproto->ports, devname); sset_find_and_delete(&ofproto->ghost_ports, devname); @@ -1681,6 +1745,7 @@ port_destruct(struct ofport *port_) if (port->stp_port) { stp_port_disable(port->stp_port); } + set_rstp_port(port_, NULL); if (ofproto->sflow) { dpif_sflow_del_port(ofproto->sflow, port->odp_port); } @@ -1773,9 +1838,11 @@ set_ipfix( struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct dpif_ipfix *di = ofproto->ipfix; bool has_options = bridge_exporter_options || flow_exporters_options; + bool new_di = false; if (has_options && !di) { di = ofproto->ipfix = dpif_ipfix_create(); + new_di = true; } if (di) { @@ -1785,6 +1852,16 @@ set_ipfix( di, bridge_exporter_options, flow_exporters_options, n_flow_exporters_options); + /* Add tunnel ports only when a new ipfix created */ + if (new_di == true) { + struct ofport_dpif *ofport; + HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) { + if (ofport->is_tunnel == true) { + dpif_ipfix_add_tunnel_port(di, &ofport->up, ofport->odp_port); + } + } + } + if (!has_options) { dpif_ipfix_unref(di); ofproto->ipfix = NULL; @@ -1824,23 +1901,23 @@ out: return error; } +static bool +cfm_status_changed(struct ofport *ofport_) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + + return ofport->cfm ? cfm_check_status_change(ofport->cfm) : true; +} + static int get_cfm_status(const struct ofport *ofport_, - struct ofproto_cfm_status *status) + struct cfm_status *status) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); int ret = 0; if (ofport->cfm) { - if (cfm_check_status_change(ofport->cfm)) { - status->faults = cfm_get_fault(ofport->cfm); - status->flap_count = cfm_get_flap_count(ofport->cfm); - status->remote_opstate = cfm_get_opup(ofport->cfm); - status->health = cfm_get_health(ofport->cfm); - cfm_get_remote_mpids(ofport->cfm, &status->rmps, &status->n_rmps); - } else { - ret = NO_STATUS_CHANGE; - } + cfm_get_status(ofport->cfm, status); } else { ret = ENOENT; } @@ -1866,6 +1943,14 @@ set_bfd(struct ofport *ofport_, const struct smap *cfg) return 0; } +static bool +bfd_status_changed(struct ofport *ofport_) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + + return ofport->bfd ? bfd_check_status_change(ofport->bfd) : true; +} + static int get_bfd_status(struct ofport *ofport_, struct smap *smap) { @@ -1873,11 +1958,7 @@ get_bfd_status(struct ofport *ofport_, struct smap *smap) int ret = 0; if (ofport->bfd) { - if (bfd_check_status_change(ofport->bfd)) { - bfd_get_status(ofport->bfd, smap); - } else { - ret = NO_STATUS_CHANGE; - } + bfd_get_status(ofport->bfd, smap); } else { ret = ENOENT; } @@ -1887,6 +1968,25 @@ get_bfd_status(struct ofport *ofport_, struct smap *smap) /* Spanning Tree. */ +/* Called while rstp_mutex is held. */ +static void +rstp_send_bpdu_cb(struct ofpbuf *pkt, void *ofport_, void *ofproto_) +{ + struct ofproto_dpif *ofproto = ofproto_; + struct ofport_dpif *ofport = ofport_; + struct eth_header *eth = ofpbuf_l2(pkt); + + netdev_get_etheraddr(ofport->up.netdev, eth->eth_src); + if (eth_addr_is_zero(eth->eth_src)) { + VLOG_WARN_RL(&rl, "%s port %d: cannot send RSTP BPDU on a port which " + "does not have a configured source MAC address.", + ofproto->up.name, ofp_to_u16(ofport->up.ofp_port)); + } else { + ofproto_dpif_send_packet(ofport, pkt); + } + ofpbuf_delete(pkt); +} + static void send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_) { @@ -1912,6 +2012,138 @@ send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_) ofpbuf_delete(pkt); } +/* Configure RSTP on 'ofproto_' using the settings defined in 's'. */ +static void +set_rstp(struct ofproto *ofproto_, const struct ofproto_rstp_settings *s) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + + /* Only revalidate flows if the configuration changed. */ + if (!s != !ofproto->rstp) { + ofproto->backer->need_revalidate = REV_RECONFIGURE; + } + + if (s) { + if (!ofproto->rstp) { + ofproto->rstp = rstp_create(ofproto_->name, s->address, + rstp_send_bpdu_cb, ofproto); + ofproto->rstp_last_tick = time_msec(); + } + rstp_set_bridge_address(ofproto->rstp, s->address); + rstp_set_bridge_priority(ofproto->rstp, s->priority); + rstp_set_bridge_ageing_time(ofproto->rstp, s->ageing_time); + rstp_set_bridge_force_protocol_version(ofproto->rstp, + s->force_protocol_version); + rstp_set_bridge_max_age(ofproto->rstp, s->bridge_max_age); + rstp_set_bridge_forward_delay(ofproto->rstp, s->bridge_forward_delay); + rstp_set_bridge_transmit_hold_count(ofproto->rstp, + s->transmit_hold_count); + } else { + struct ofport *ofport; + HMAP_FOR_EACH (ofport, hmap_node, &ofproto->up.ports) { + set_rstp_port(ofport, NULL); + } + rstp_unref(ofproto->rstp); + ofproto->rstp = NULL; + } +} + +static void +get_rstp_status(struct ofproto *ofproto_, struct ofproto_rstp_status *s) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + + if (ofproto->rstp) { + s->enabled = true; + s->root_id = rstp_get_root_id(ofproto->rstp); + s->bridge_id = rstp_get_bridge_id(ofproto->rstp); + s->designated_id = rstp_get_designated_id(ofproto->rstp); + s->root_path_cost = rstp_get_root_path_cost(ofproto->rstp); + s->designated_port_id = rstp_get_designated_port_id(ofproto->rstp); + s->bridge_port_id = rstp_get_bridge_port_id(ofproto->rstp); + } else { + s->enabled = false; + } +} + +static void +update_rstp_port_state(struct ofport_dpif *ofport) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + enum rstp_state state; + + /* Figure out new state. */ + state = ofport->rstp_port ? rstp_port_get_state(ofport->rstp_port) + : RSTP_DISABLED; + + /* Update state. */ + if (ofport->rstp_state != state) { + enum ofputil_port_state of_state; + bool fwd_change; + + VLOG_DBG("port %s: RSTP state changed from %s to %s", + netdev_get_name(ofport->up.netdev), + rstp_state_name(ofport->rstp_state), + rstp_state_name(state)); + if (rstp_learn_in_state(ofport->rstp_state) + != rstp_learn_in_state(state)) { + /* xxx Learning action flows should also be flushed. */ + ovs_rwlock_wrlock(&ofproto->ml->rwlock); + mac_learning_flush(ofproto->ml); + ovs_rwlock_unlock(&ofproto->ml->rwlock); + } + fwd_change = rstp_forward_in_state(ofport->rstp_state) + != rstp_forward_in_state(state); + + ofproto->backer->need_revalidate = REV_RSTP; + ofport->rstp_state = state; + + if (fwd_change && ofport->bundle) { + bundle_update(ofport->bundle); + } + + /* Update the RSTP state bits in the OpenFlow port description. */ + of_state = ofport->up.pp.state & ~OFPUTIL_PS_STP_MASK; + of_state |= (state == RSTP_LEARNING ? OFPUTIL_PS_STP_LEARN + : state == RSTP_FORWARDING ? OFPUTIL_PS_STP_FORWARD + : state == RSTP_DISCARDING ? OFPUTIL_PS_STP_LISTEN + : 0); + ofproto_port_set_state(&ofport->up, of_state); + } +} + +static void +rstp_run(struct ofproto_dpif *ofproto) +{ + if (ofproto->rstp) { + long long int now = time_msec(); + long long int elapsed = now - ofproto->rstp_last_tick; + struct rstp_port *rp; + struct ofport_dpif *ofport; + + /* Every second, decrease the values of the timers. */ + if (elapsed >= 1000) { + rstp_tick_timers(ofproto->rstp); + ofproto->rstp_last_tick = now; + } + rp = NULL; + while ((ofport = rstp_get_next_changed_port_aux(ofproto->rstp, &rp))) { + update_rstp_port_state(ofport); + } + /* FIXME: This check should be done on-event (i.e., when setting + * p->fdb_flush) and not periodically. + */ + if (rstp_check_and_reset_fdb_flush(ofproto->rstp)) { + ovs_rwlock_wrlock(&ofproto->ml->rwlock); + /* FIXME: RSTP should be able to flush the entries pertaining to a + * single port, not the whole table. + */ + mac_learning_flush(ofproto->ml); + ovs_rwlock_unlock(&ofproto->ml->rwlock); + } + } +} + /* Configures STP on 'ofproto_' using the settings defined in 's'. */ static int set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s) @@ -1981,16 +2213,17 @@ update_stp_port_state(struct ofport_dpif *ofport) enum ofputil_port_state of_state; bool fwd_change; - VLOG_DBG_RL(&rl, "port %s: STP state changed from %s to %s", - netdev_get_name(ofport->up.netdev), - stp_state_name(ofport->stp_state), - stp_state_name(state)); + VLOG_DBG("port %s: STP state changed from %s to %s", + netdev_get_name(ofport->up.netdev), + stp_state_name(ofport->stp_state), + stp_state_name(state)); if (stp_learn_in_state(ofport->stp_state) != stp_learn_in_state(state)) { /* xxx Learning action flows should also be flushed. */ ovs_rwlock_wrlock(&ofproto->ml->rwlock); mac_learning_flush(ofproto->ml); ovs_rwlock_unlock(&ofproto->ml->rwlock); + mcast_snooping_mdb_flush(ofproto->ms); } fwd_change = stp_forward_in_state(ofport->stp_state) != stp_forward_in_state(state); @@ -2033,13 +2266,17 @@ set_stp_port(struct ofport *ofport_, } return 0; } else if (sp && stp_port_no(sp) != s->port_num - && ofport == stp_port_get_aux(sp)) { + && ofport == stp_port_get_aux(sp)) { /* The port-id changed, so disable the old one if it's not * already in use by another port. */ stp_port_disable(sp); } sp = ofport->stp_port = stp_get_port(ofproto->stp, s->port_num); + + /* Set name before enabling the port so that debugging messages can print + * the name. */ + stp_port_set_name(sp, netdev_get_name(ofport->up.netdev)); stp_port_enable(sp); stp_port_set_aux(sp, ofport); @@ -2116,6 +2353,7 @@ stp_run(struct ofproto_dpif *ofproto) ovs_rwlock_wrlock(&ofproto->ml->rwlock); mac_learning_flush(ofproto->ml); ovs_rwlock_unlock(&ofproto->ml->rwlock); + mcast_snooping_mdb_flush(ofproto->ms); } } } @@ -2127,6 +2365,57 @@ stp_wait(struct ofproto_dpif *ofproto) poll_timer_wait(1000); } } + +/* Configures RSTP on 'ofport_' using the settings defined in 's'. The + * caller is responsible for assigning RSTP port numbers and ensuring + * there are no duplicates. */ +static void +set_rstp_port(struct ofport *ofport_, + const struct ofproto_port_rstp_settings *s) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + struct rstp_port *rp = ofport->rstp_port; + + if (!s || !s->enable) { + if (rp) { + rstp_port_unref(rp); + ofport->rstp_port = NULL; + update_rstp_port_state(ofport); + } + return; + } + + /* Check if need to add a new port. */ + if (!rp) { + rp = ofport->rstp_port = rstp_add_port(ofproto->rstp); + } + + rstp_port_set(rp, s->port_num, s->priority, s->path_cost, + s->admin_edge_port, s->auto_edge, s->mcheck, ofport); + update_rstp_port_state(ofport); + /* Synchronize operational status. */ + rstp_port_set_mac_operational(rp, ofport->may_enable); +} + +static void +get_rstp_port_status(struct ofport *ofport_, + struct ofproto_port_rstp_status *s) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + struct rstp_port *rp = ofport->rstp_port; + + if (!ofproto->rstp || !rp) { + s->enabled = false; + return; + } + + s->enabled = true; + rstp_port_get_status(rp, &s->port_id, &s->state, &s->role, &s->tx_count, + &s->rx_count, &s->error_count, &s->uptime); +} + static int set_queues(struct ofport *ofport_, const struct ofproto_port_queue *qdscp, @@ -2290,9 +2579,9 @@ bundle_destroy(struct ofbundle *bundle) ofproto = bundle->ofproto; mbridge_unregister_bundle(ofproto->mbridge, bundle->aux); - ovs_rwlock_wrlock(&xlate_rwlock); + xlate_txn_start(); xlate_bundle_remove(bundle); - ovs_rwlock_unlock(&xlate_rwlock); + xlate_txn_commit(); LIST_FOR_EACH_SAFE (port, next_port, bundle_node, &bundle->ports) { bundle_del_port(port); @@ -2700,6 +2989,56 @@ set_mac_table_config(struct ofproto *ofproto_, unsigned int idle_time, mac_learning_set_max_entries(ofproto->ml, max_entries); ovs_rwlock_unlock(&ofproto->ml->rwlock); } + +/* Configures multicast snooping on 'ofport' using the settings + * defined in 's'. */ +static int +set_mcast_snooping(struct ofproto *ofproto_, + const struct ofproto_mcast_snooping_settings *s) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + + /* Only revalidate flows if the configuration changed. */ + if (!s != !ofproto->ms) { + ofproto->backer->need_revalidate = REV_RECONFIGURE; + } + + if (s) { + if (!ofproto->ms) { + ofproto->ms = mcast_snooping_create(); + } + + ovs_rwlock_wrlock(&ofproto->ms->rwlock); + mcast_snooping_set_idle_time(ofproto->ms, s->idle_time); + mcast_snooping_set_max_entries(ofproto->ms, s->max_entries); + if (mcast_snooping_set_flood_unreg(ofproto->ms, s->flood_unreg)) { + ofproto->backer->need_revalidate = REV_RECONFIGURE; + } + ovs_rwlock_unlock(&ofproto->ms->rwlock); + } else { + mcast_snooping_unref(ofproto->ms); + ofproto->ms = NULL; + } + + return 0; +} + +/* Configures multicast snooping port's flood setting on 'ofproto'. */ +static int +set_mcast_snooping_port(struct ofproto *ofproto_, void *aux, bool flood) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + struct ofbundle *bundle = bundle_lookup(ofproto, aux); + + if (ofproto->ms) { + ovs_rwlock_wrlock(&ofproto->ms->rwlock); + mcast_snooping_set_port_flood(ofproto->ms, bundle->vlan, bundle, + flood); + ovs_rwlock_unlock(&ofproto->ms->rwlock); + } + return 0; +} + /* Ports. */ @@ -2810,7 +3149,12 @@ port_run(struct ofport_dpif *ofport) if (ofport->may_enable != enable) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + ofproto->backer->need_revalidate = REV_PORT_TOGGLED; + + if (ofport->rstp_port) { + rstp_port_set_mac_operational(ofport->rstp_port, enable); + } } ofport->may_enable = enable; @@ -3068,8 +3412,6 @@ rule_expire(struct rule_dpif *rule) long long int now = time_msec(); int reason = -1; - ovs_assert(!rule->up.pending); - hard_timeout = rule->up.hard_timeout; idle_timeout = rule->up.idle_timeout; @@ -3128,24 +3470,25 @@ ofproto_dpif_execute_actions(struct ofproto_dpif *ofproto, rule_dpif_credit_stats(rule, &stats); } - xlate_in_init(&xin, ofproto, flow, rule, stats.tcp_flags, packet); + xlate_in_init(&xin, ofproto, flow, flow->in_port.ofp_port, rule, + stats.tcp_flags, packet); xin.ofpacts = ofpacts; xin.ofpacts_len = ofpacts_len; xin.resubmit_stats = &stats; xlate_actions(&xin, &xout); + execute.actions = ofpbuf_data(xout.odp_actions); + execute.actions_len = ofpbuf_size(xout.odp_actions); + execute.packet = packet; + execute.md = pkt_metadata_from_flow(flow); + execute.needs_help = (xout.slow & SLOW_ACTION) != 0; + + /* Fix up in_port. */ in_port = flow->in_port.ofp_port; if (in_port == OFPP_NONE) { in_port = OFPP_LOCAL; } - execute.actions = ofpbuf_data(&xout.odp_actions); - execute.actions_len = ofpbuf_size(&xout.odp_actions); - execute.packet = packet; - execute.md.tunnel = flow->tunnel; - execute.md.skb_priority = flow->skb_priority; - execute.md.pkt_mark = flow->pkt_mark; execute.md.in_port.odp_port = ofp_port_to_odp_port(ofproto, in_port); - execute.needs_help = (xout.slow & SLOW_ACTION) != 0; error = dpif_execute(ofproto->backer->dpif, &execute); @@ -3188,6 +3531,39 @@ rule_dpif_get_actions(const struct rule_dpif *rule) return rule_get_actions(&rule->up); } +/* Sets 'rule''s recirculation id. */ +static void +rule_dpif_set_recirc_id(struct rule_dpif *rule, uint32_t id) + OVS_REQUIRES(rule->up.mutex) +{ + ovs_assert(!rule->recirc_id); + rule->recirc_id = id; +} + +/* Returns 'rule''s recirculation id. */ +uint32_t +rule_dpif_get_recirc_id(struct rule_dpif *rule) + OVS_REQUIRES(rule->up.mutex) +{ + if (!rule->recirc_id) { + struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto); + + rule_dpif_set_recirc_id(rule, ofproto_dpif_alloc_recirc_id(ofproto)); + } + return rule->recirc_id; +} + +/* Sets 'rule''s recirculation id. */ +void +rule_set_recirc_id(struct rule *rule_, uint32_t id) +{ + struct rule_dpif *rule = rule_dpif_cast(rule_); + + ovs_mutex_lock(&rule->up.mutex); + rule_dpif_set_recirc_id(rule, id); + ovs_mutex_unlock(&rule->up.mutex); +} + /* Lookup 'flow' in table 0 of 'ofproto''s classifier. * If 'wc' is non-null, sets the fields that were relevant as part of * the lookup. Returns the table_id where a match or miss occurred. @@ -3203,7 +3579,7 @@ rule_dpif_get_actions(const struct rule_dpif *rule) uint8_t rule_dpif_lookup(struct ofproto_dpif *ofproto, struct flow *flow, struct flow_wildcards *wc, struct rule_dpif **rule, - bool take_ref) + bool take_ref, const struct dpif_flow_stats *stats) { enum rule_dpif_lookup_verdict verdict; enum ofputil_port_config config = 0; @@ -3230,7 +3606,7 @@ rule_dpif_lookup(struct ofproto_dpif *ofproto, struct flow *flow, } verdict = rule_dpif_lookup_from_table(ofproto, flow, wc, true, - &table_id, rule, take_ref); + &table_id, rule, take_ref, stats); switch (verdict) { case RULE_DPIF_LOOKUP_VERDICT_MATCH: @@ -3274,39 +3650,39 @@ rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto, uint8_t table_id, struct classifier *cls = &ofproto->up.tables[table_id].cls; const struct cls_rule *cls_rule; struct rule_dpif *rule; + struct flow ofpc_normal_flow; - fat_rwlock_rdlock(&cls->rwlock); if (ofproto->up.frag_handling != OFPC_FRAG_NX_MATCH) { - if (wc) { - memset(&wc->masks.dl_type, 0xff, sizeof wc->masks.dl_type); - if (is_ip_any(flow)) { - wc->masks.nw_frag |= FLOW_NW_FRAG_MASK; - } - } + /* We always unwildcard dl_type and nw_frag (for IP), so they + * need not be unwildcarded here. */ if (flow->nw_frag & FLOW_NW_FRAG_ANY) { if (ofproto->up.frag_handling == OFPC_FRAG_NORMAL) { /* We must pretend that transport ports are unavailable. */ - struct flow ofpc_normal_flow = *flow; + ofpc_normal_flow = *flow; ofpc_normal_flow.tp_src = htons(0); ofpc_normal_flow.tp_dst = htons(0); - cls_rule = classifier_lookup(cls, &ofpc_normal_flow, wc); + flow = &ofpc_normal_flow; } else { - /* Must be OFPC_FRAG_DROP (we don't have OFPC_FRAG_REASM). */ + /* Must be OFPC_FRAG_DROP (we don't have OFPC_FRAG_REASM). + * Use the drop_frags_rule (which cannot disappear). */ cls_rule = &ofproto->drop_frags_rule->up.cr; + rule = rule_dpif_cast(rule_from_cls_rule(cls_rule)); + if (take_ref) { + rule_dpif_ref(rule); + } + return rule; } - } else { - cls_rule = classifier_lookup(cls, flow, wc); } - } else { - cls_rule = classifier_lookup(cls, flow, wc); } - rule = rule_dpif_cast(rule_from_cls_rule(cls_rule)); - if (take_ref) { - rule_dpif_ref(rule); - } - fat_rwlock_unlock(&cls->rwlock); + do { + cls_rule = classifier_lookup(cls, flow, wc); + + rule = rule_dpif_cast(rule_from_cls_rule(cls_rule)); + + /* Try again if the rule was released before we get the reference. */ + } while (rule && take_ref && !rule_dpif_try_ref(rule)); return rule; } @@ -3352,7 +3728,7 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, struct flow_wildcards *wc, bool honor_table_miss, uint8_t *table_id, struct rule_dpif **rule, - bool take_ref) + bool take_ref, const struct dpif_flow_stats *stats) { uint8_t next_id; @@ -3363,22 +3739,29 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, *table_id = next_id; *rule = rule_dpif_lookup_in_table(ofproto, *table_id, flow, wc, take_ref); + if (stats) { + struct oftable *tbl = &ofproto->up.tables[next_id]; + unsigned long orig; + + atomic_add_relaxed(*rule ? &tbl->n_matched : &tbl->n_missed, + stats->n_packets, &orig); + } if (*rule) { return RULE_DPIF_LOOKUP_VERDICT_MATCH; } else if (!honor_table_miss) { return RULE_DPIF_LOOKUP_VERDICT_CONTROLLER; } else { - switch (ofproto_table_get_config(&ofproto->up, *table_id)) { - case OFPROTO_TABLE_MISS_CONTINUE: + switch (ofproto_table_get_miss_config(&ofproto->up, *table_id)) { + case OFPUTIL_TABLE_MISS_CONTINUE: break; - case OFPROTO_TABLE_MISS_CONTROLLER: + case OFPUTIL_TABLE_MISS_CONTROLLER: return RULE_DPIF_LOOKUP_VERDICT_CONTROLLER; - case OFPROTO_TABLE_MISS_DROP: + case OFPUTIL_TABLE_MISS_DROP: return RULE_DPIF_LOOKUP_VERDICT_DROP; - case OFPROTO_TABLE_MISS_DEFAULT: + case OFPUTIL_TABLE_MISS_DEFAULT: return RULE_DPIF_LOOKUP_VERDICT_DEFAULT; } } @@ -3413,7 +3796,6 @@ complete_operation(struct rule_dpif *rule) struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto); ofproto->backer->need_revalidate = REV_FLOW_TABLE; - ofoperation_complete(rule->up.pending, 0); } static struct rule_dpif *rule_dpif_cast(const struct rule *rule) @@ -3444,15 +3826,18 @@ rule_construct(struct rule *rule_) rule->stats.n_packets = 0; rule->stats.n_bytes = 0; rule->stats.used = rule->up.modified; + rule->recirc_id = 0; + return 0; } -static void +static enum ofperr rule_insert(struct rule *rule_) OVS_REQUIRES(ofproto_mutex) { struct rule_dpif *rule = rule_dpif_cast(rule_); complete_operation(rule); + return 0; } static void @@ -3467,7 +3852,13 @@ static void rule_destruct(struct rule *rule_) { struct rule_dpif *rule = rule_dpif_cast(rule_); + ovs_mutex_destroy(&rule->stats_mutex); + if (rule->recirc_id) { + struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto); + + ofproto_dpif_free_recirc_id(ofproto, rule->recirc_id); + } } static void @@ -3653,7 +4044,6 @@ group_dpif_lookup(struct ofproto_dpif *ofproto, uint32_t group_id, struct ofgroup *ofgroup; bool found; - *group = NULL; found = ofproto_group_lookup(&ofproto->up, group_id, &ofgroup); *group = found ? group_dpif_cast(ofgroup) : NULL; @@ -3788,6 +4178,36 @@ ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, int argc, unixctl_command_reply(conn, "table successfully flushed"); } +static void +ofproto_unixctl_mcast_snooping_flush(struct unixctl_conn *conn, int argc, + const char *argv[], void *aux OVS_UNUSED) +{ + struct ofproto_dpif *ofproto; + + if (argc > 1) { + ofproto = ofproto_dpif_lookup(argv[1]); + if (!ofproto) { + unixctl_command_reply_error(conn, "no such bridge"); + return; + } + + if (!mcast_snooping_enabled(ofproto->ms)) { + unixctl_command_reply_error(conn, "multicast snooping is disabled"); + return; + } + mcast_snooping_mdb_flush(ofproto->ms); + } else { + HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { + if (!mcast_snooping_enabled(ofproto->ms)) { + continue; + } + mcast_snooping_mdb_flush(ofproto->ms); + } + } + + unixctl_command_reply(conn, "table successfully flushed"); +} + static struct ofport_dpif * ofbundle_get_a_port(const struct ofbundle *bundle) { @@ -3826,6 +4246,61 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED, ds_destroy(&ds); } +static void +ofproto_unixctl_mcast_snooping_show(struct unixctl_conn *conn, + int argc OVS_UNUSED, + const char *argv[], + void *aux OVS_UNUSED) +{ + struct ds ds = DS_EMPTY_INITIALIZER; + const struct ofproto_dpif *ofproto; + const struct ofbundle *bundle; + const struct mcast_group *grp; + struct mcast_group_bundle *b; + struct mcast_mrouter_bundle *mrouter; + + ofproto = ofproto_dpif_lookup(argv[1]); + if (!ofproto) { + unixctl_command_reply_error(conn, "no such bridge"); + return; + } + + if (!mcast_snooping_enabled(ofproto->ms)) { + unixctl_command_reply_error(conn, "multicast snooping is disabled"); + return; + } + + ds_put_cstr(&ds, " port VLAN GROUP Age\n"); + ovs_rwlock_rdlock(&ofproto->ms->rwlock); + LIST_FOR_EACH (grp, group_node, &ofproto->ms->group_lru) { + LIST_FOR_EACH(b, bundle_node, &grp->bundle_lru) { + char name[OFP_MAX_PORT_NAME_LEN]; + + bundle = b->port; + ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port, + name, sizeof name); + ds_put_format(&ds, "%5s %4d "IP_FMT" %3d\n", + name, grp->vlan, IP_ARGS(grp->ip4), + mcast_bundle_age(ofproto->ms, b)); + } + } + + /* ports connected to multicast routers */ + LIST_FOR_EACH(mrouter, mrouter_node, &ofproto->ms->mrouter_lru) { + char name[OFP_MAX_PORT_NAME_LEN]; + + bundle = mrouter->port; + ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port, + name, sizeof name); + ds_put_format(&ds, "%5s %4d querier %3d\n", + name, mrouter->vlan, + mcast_mrouter_age(ofproto->ms, mrouter)); + } + ovs_rwlock_unlock(&ofproto->ms->rwlock); + unixctl_command_reply(conn, ds_cstr(&ds)); + ds_destroy(&ds); +} + struct trace_ctx { struct xlate_out xout; struct xlate_in xin; @@ -3899,7 +4374,7 @@ static void trace_format_odp(struct ds *result, int level, const char *title, struct trace_ctx *trace) { - struct ofpbuf *odp_actions = &trace->xout.odp_actions; + struct ofpbuf *odp_actions = trace->xout.odp_actions; ds_put_char_multiple(result, '\t', level); ds_put_format(result, "%s: ", title); @@ -4028,12 +4503,21 @@ parse_flow_and_packet(int argc, const char *argv[], goto exit; } - if (xlate_receive(backer, NULL, ofpbuf_data(&odp_key), - ofpbuf_size(&odp_key), flow, - ofprotop, NULL, NULL, NULL, NULL)) { + if (odp_flow_key_to_flow(ofpbuf_data(&odp_key), ofpbuf_size(&odp_key), + flow) == ODP_FIT_ERROR) { + error = "Failed to parse flow key"; + goto exit; + } + + *ofprotop = xlate_lookup_ofproto(backer, flow, + &flow->in_port.ofp_port); + if (*ofprotop == NULL) { error = "Invalid datapath flow"; goto exit; } + + vsp_adjust_flow(*ofprotop, flow, NULL); + } else { char *err = parse_ofp_exact_flow(flow, NULL, argv[argc - 1], NULL); @@ -4129,7 +4613,7 @@ ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc, ofpbuf_init(&ofpacts, 0); /* Parse actions. */ - error = parse_ofpacts(argv[--argc], &ofpacts, &usable_protocols); + error = ofpacts_parse_actions(argv[--argc], &ofpacts, &usable_protocols); if (error) { unixctl_command_reply_error(conn, error); free(error); @@ -4223,7 +4707,7 @@ ofproto_trace(struct ofproto_dpif *ofproto, struct flow *flow, if (ofpacts) { rule = NULL; } else { - rule_dpif_lookup(ofproto, flow, &trace.wc, &rule, false); + rule_dpif_lookup(ofproto, flow, &trace.wc, &rule, false, NULL); trace_format_rule(ds, 0, rule); if (rule == ofproto->miss_rule) { @@ -4241,8 +4725,8 @@ ofproto_trace(struct ofproto_dpif *ofproto, struct flow *flow, trace.result = ds; trace.key = flow; /* Original flow key, used for megaflow. */ trace.flow = *flow; /* May be modified by actions. */ - xlate_in_init(&trace.xin, ofproto, flow, rule, ntohs(flow->tcp_flags), - packet); + xlate_in_init(&trace.xin, ofproto, flow, flow->in_port.ofp_port, rule, + ntohs(flow->tcp_flags), packet); if (ofpacts) { trace.xin.ofpacts = ofpacts; trace.xin.ofpacts_len = ofpacts_len; @@ -4257,8 +4741,8 @@ ofproto_trace(struct ofproto_dpif *ofproto, struct flow *flow, trace_format_megaflow(ds, 0, "Megaflow", &trace); ds_put_cstr(ds, "Datapath actions: "); - format_odp_actions(ds, ofpbuf_data(&trace.xout.odp_actions), - ofpbuf_size(&trace.xout.odp_actions)); + format_odp_actions(ds, ofpbuf_data(trace.xout.odp_actions), + ofpbuf_size(trace.xout.odp_actions)); if (trace.xout.slow) { enum slow_path_reason slow; @@ -4408,18 +4892,6 @@ ofproto_unixctl_dpif_show(struct unixctl_conn *conn, int argc OVS_UNUSED, ds_destroy(&ds); } -static bool -ofproto_dpif_contains_flow(const struct ofproto_dpif *ofproto, - const struct nlattr *key, size_t key_len) -{ - struct ofproto_dpif *ofp; - struct flow flow; - - xlate_receive(ofproto->backer, NULL, key, key_len, &flow, &ofp, - NULL, NULL, NULL, NULL); - return ofp == ofproto; -} - static void ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], @@ -4458,7 +4930,10 @@ ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn, flow_dump = dpif_flow_dump_create(ofproto->backer->dpif); flow_dump_thread = dpif_flow_dump_thread_create(flow_dump); while (dpif_flow_dump_next(flow_dump_thread, &f, 1)) { - if (!ofproto_dpif_contains_flow(ofproto, f.key, f.key_len)) { + struct flow flow; + + if (odp_flow_key_to_flow(f.key, f.key_len, &flow) == ODP_FIT_ERROR + || xlate_lookup_ofproto(ofproto->backer, &flow, NULL) != ofproto) { continue; } @@ -4506,6 +4981,10 @@ ofproto_dpif_unixctl_init(void) ofproto_unixctl_fdb_flush, NULL); unixctl_command_register("fdb/show", "bridge", 1, 1, ofproto_unixctl_fdb_show, NULL); + unixctl_command_register("mdb/flush", "[bridge]", 0, 1, + ofproto_unixctl_mcast_snooping_flush, NULL); + unixctl_command_register("mdb/show", "bridge", 1, 1, + ofproto_unixctl_mcast_snooping_show, NULL); unixctl_command_register("dpif/dump-dps", "", 0, 0, ofproto_unixctl_dpif_dump_dps, NULL); unixctl_command_register("dpif/show", "", 0, 0, ofproto_unixctl_dpif_show, @@ -4668,11 +5147,13 @@ vsp_vlandev_to_realdev(const struct ofproto_dpif *ofproto, /* Given 'flow', a flow representing a packet received on 'ofproto', checks * whether 'flow->in_port' represents a Linux VLAN device. If so, changes * 'flow->in_port' to the "real" device backing the VLAN device, sets - * 'flow->vlan_tci' to the VLAN VID, and returns true. Otherwise (which is - * always the case unless VLAN splinters are enabled), returns false without - * making any changes. */ + * 'flow->vlan_tci' to the VLAN VID, and returns true. Optionally pushes the + * appropriate VLAN on 'packet' if provided. Otherwise (which is always the + * case unless VLAN splinters are enabled), returns false without making any + * changes. */ bool -vsp_adjust_flow(const struct ofproto_dpif *ofproto, struct flow *flow) +vsp_adjust_flow(const struct ofproto_dpif *ofproto, struct flow *flow, + struct ofpbuf *packet) OVS_EXCLUDED(ofproto->vsp_mutex) { ofp_port_t realdev; @@ -4694,6 +5175,15 @@ vsp_adjust_flow(const struct ofproto_dpif *ofproto, struct flow *flow) * the VLAN device's VLAN ID. */ flow->in_port.ofp_port = realdev; flow->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI); + + if (packet) { + /* Make the packet resemble the flow, so that it gets sent to an + * OpenFlow controller properly, so that it looks correct for sFlow, + * and so that flow_extract() will get the correct vlan_tci if it is + * called on 'packet'. */ + eth_push_vlan(packet, htons(ETH_TYPE_VLAN), flow->vlan_tci); + } + return true; } @@ -4802,6 +5292,7 @@ ofproto_dpif_free_recirc_id(struct ofproto_dpif *ofproto, uint32_t recirc_id) int ofproto_dpif_add_internal_flow(struct ofproto_dpif *ofproto, const struct match *match, int priority, + uint16_t idle_timeout, const struct ofpbuf *ofpacts, struct rule **rulep) { @@ -4817,7 +5308,7 @@ ofproto_dpif_add_internal_flow(struct ofproto_dpif *ofproto, fm.modify_cookie = false; fm.table_id = TBL_INTERNAL; fm.command = OFPFC_ADD; - fm.idle_timeout = 0; + fm.idle_timeout = idle_timeout; fm.hard_timeout = 0; fm.buffer_id = 0; fm.out_port = 0; @@ -4887,8 +5378,7 @@ const struct ofproto_class ofproto_dpif_class = { NULL, /* get_memory_usage. */ type_get_memory_usage, flush, - get_features, - get_tables, + query_tables, port_alloc, port_construct, port_destruct, @@ -4914,6 +5404,7 @@ const struct ofproto_class ofproto_dpif_class = { rule_dealloc, rule_get_stats, rule_execute, + NULL, /* rule_premodify_actions */ rule_modify_actions, set_frag_handling, packet_out, @@ -4922,14 +5413,20 @@ const struct ofproto_class ofproto_dpif_class = { set_sflow, set_ipfix, set_cfm, + cfm_status_changed, get_cfm_status, set_bfd, + bfd_status_changed, get_bfd_status, set_stp, get_stp_status, set_stp_port, get_stp_port_status, get_stp_port_stats, + set_rstp, + get_rstp_status, + set_rstp_port, + get_rstp_port_status, set_queues, bundle_set, bundle_remove, @@ -4939,6 +5436,8 @@ const struct ofproto_class ofproto_dpif_class = { is_mirror_output_bundle, forward_bpdu_changed, set_mac_table_config, + set_mcast_snooping, + set_mcast_snooping_port, set_realdev, NULL, /* meter_get_features */ NULL, /* meter_set */