+ if (xbundle->bond != bond) {
+ bond_unref(xbundle->bond);
+ xbundle->bond = bond_ref(bond);
+ }
+
+ if (xbundle->lacp != lacp) {
+ lacp_unref(xbundle->lacp);
+ xbundle->lacp = lacp_ref(lacp);
+ }
+}
+
+void
+xlate_bundle_remove(struct ofbundle *ofbundle)
+{
+ struct xbundle *xbundle = xbundle_lookup(ofbundle);
+ struct xport *xport, *next;
+
+ if (!xbundle) {
+ return;
+ }
+
+ LIST_FOR_EACH_SAFE (xport, next, bundle_node, &xbundle->xports) {
+ list_remove(&xport->bundle_node);
+ xport->xbundle = NULL;
+ }
+
+ hmap_remove(&xbundles, &xbundle->hmap_node);
+ list_remove(&xbundle->list_node);
+ bond_unref(xbundle->bond);
+ lacp_unref(xbundle->lacp);
+ free(xbundle->name);
+ free(xbundle);
+}
+
+void
+xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
+ struct ofport_dpif *ofport, ofp_port_t ofp_port,
+ odp_port_t odp_port, const struct netdev *netdev,
+ const struct cfm *cfm, const struct bfd *bfd,
+ struct ofport_dpif *peer, int stp_port_no,
+ const struct ofproto_port_queue *qdscp_list, size_t n_qdscp,
+ enum ofputil_port_config config,
+ enum ofputil_port_state state, bool is_tunnel,
+ bool may_enable)
+{
+ struct xport *xport = xport_lookup(ofport);
+ size_t i;
+
+ if (!xport) {
+ xport = xzalloc(sizeof *xport);
+ xport->ofport = ofport;
+ xport->xbridge = xbridge_lookup(ofproto);
+ xport->ofp_port = ofp_port;
+
+ hmap_init(&xport->skb_priorities);
+ hmap_insert(&xports, &xport->hmap_node, hash_pointer(ofport, 0));
+ hmap_insert(&xport->xbridge->xports, &xport->ofp_node,
+ hash_ofp_port(xport->ofp_port));
+ }
+
+ ovs_assert(xport->ofp_port == ofp_port);
+
+ xport->config = config;
+ xport->state = state;
+ xport->stp_port_no = stp_port_no;
+ xport->is_tunnel = is_tunnel;
+ xport->may_enable = may_enable;
+ xport->odp_port = odp_port;
+
+ if (xport->netdev != netdev) {
+ netdev_close(xport->netdev);
+ xport->netdev = netdev_ref(netdev);
+ }
+
+ if (xport->cfm != cfm) {
+ cfm_unref(xport->cfm);
+ xport->cfm = cfm_ref(cfm);
+ }
+
+ if (xport->bfd != bfd) {
+ bfd_unref(xport->bfd);
+ xport->bfd = bfd_ref(bfd);
+ }
+
+ if (xport->peer) {
+ xport->peer->peer = NULL;
+ }
+ xport->peer = xport_lookup(peer);
+ if (xport->peer) {
+ xport->peer->peer = xport;
+ }
+
+ if (xport->xbundle) {
+ list_remove(&xport->bundle_node);
+ }
+ xport->xbundle = xbundle_lookup(ofbundle);
+ if (xport->xbundle) {
+ list_insert(&xport->xbundle->xports, &xport->bundle_node);
+ }
+
+ clear_skb_priorities(xport);
+ for (i = 0; i < n_qdscp; i++) {
+ struct skb_priority_to_dscp *pdscp;
+ uint32_t skb_priority;
+
+ if (dpif_queue_to_priority(xport->xbridge->dpif, qdscp_list[i].queue,
+ &skb_priority)) {
+ continue;
+ }
+
+ pdscp = xmalloc(sizeof *pdscp);
+ pdscp->skb_priority = skb_priority;
+ pdscp->dscp = (qdscp_list[i].dscp << 2) & IP_DSCP_MASK;
+ hmap_insert(&xport->skb_priorities, &pdscp->hmap_node,
+ hash_int(pdscp->skb_priority, 0));
+ }
+}
+
+void
+xlate_ofport_remove(struct ofport_dpif *ofport)
+{
+ struct xport *xport = xport_lookup(ofport);
+
+ if (!xport) {
+ return;
+ }
+
+ if (xport->peer) {
+ xport->peer->peer = NULL;
+ xport->peer = NULL;
+ }
+
+ if (xport->xbundle) {
+ list_remove(&xport->bundle_node);
+ }
+
+ clear_skb_priorities(xport);
+ hmap_destroy(&xport->skb_priorities);
+
+ hmap_remove(&xports, &xport->hmap_node);
+ hmap_remove(&xport->xbridge->xports, &xport->ofp_node);
+
+ netdev_close(xport->netdev);
+ cfm_unref(xport->cfm);
+ bfd_unref(xport->bfd);
+ free(xport);
+}
+
+/* Given a datpath, packet, and flow metadata ('backer', 'packet', and 'key'
+ * respectively), populates 'flow' with the result of odp_flow_key_to_flow().
+ * Optionally, if nonnull, populates 'fitnessp' with the fitness of 'flow' as
+ * returned by odp_flow_key_to_flow(). Also, optionally populates 'ofproto'
+ * with the ofproto_dpif, and 'odp_in_port' with the datapath in_port, that
+ * 'packet' ingressed.
+ *
+ * If 'ofproto' is nonnull, requires 'flow''s in_port to exist. Otherwise sets
+ * 'flow''s in_port to OFPP_NONE.
+ *
+ * This function does post-processing on data returned from
+ * odp_flow_key_to_flow() to help make VLAN splinters transparent to the rest
+ * of the upcall processing logic. In particular, if the extracted in_port is
+ * a VLAN splinter port, it replaces flow->in_port by the "real" port, sets
+ * flow->vlan_tci correctly for the VLAN of the VLAN splinter port, and pushes
+ * a VLAN header onto 'packet' (if it is nonnull).
+ *
+ * Similarly, this function also includes some logic to help with tunnels. It
+ * may modify 'flow' as necessary to make the tunneling implementation
+ * transparent to the upcall processing logic.
+ *
+ * Returns 0 if successful, ENODEV if the parsed flow has no associated ofport,
+ * or some other positive errno if there are other problems. */
+int
+xlate_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
+ const struct nlattr *key, size_t key_len,
+ struct flow *flow, enum odp_key_fitness *fitnessp,
+ struct ofproto_dpif **ofproto, odp_port_t *odp_in_port)
+{
+ enum odp_key_fitness fitness;
+ const struct xport *xport;
+ int error = ENODEV;
+
+ ovs_rwlock_rdlock(&xlate_rwlock);
+ fitness = odp_flow_key_to_flow(key, key_len, flow);
+ if (fitness == ODP_FIT_ERROR) {
+ error = EINVAL;
+ goto exit;
+ }
+
+ if (odp_in_port) {
+ *odp_in_port = flow->in_port.odp_port;
+ }
+
+ xport = xport_lookup(tnl_port_should_receive(flow)
+ ? tnl_port_receive(flow)
+ : odp_port_to_ofport(backer, flow->in_port.odp_port));
+
+ flow->in_port.ofp_port = xport ? xport->ofp_port : OFPP_NONE;
+ if (!xport) {
+ goto exit;
+ }
+
+ if (vsp_adjust_flow(xport->xbridge->ofproto, flow)) {
+ 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'.
+ *
+ * The allocated space inside 'packet' probably also contains
+ * 'key', that is, both 'packet' and 'key' are probably part of
+ * a struct dpif_upcall (see the large comment on that
+ * structure definition), so pushing data on 'packet' is in
+ * general not a good idea since it could overwrite 'key' or
+ * free it as a side effect. However, it's OK in this special
+ * case because we know that 'packet' is inside a Netlink
+ * attribute: pushing 4 bytes will just overwrite the 4-byte
+ * "struct nlattr", which is fine since we don't need that
+ * header anymore. */
+ eth_push_vlan(packet, flow->vlan_tci);
+ }
+ /* We can't reproduce 'key' from 'flow'. */
+ fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
+ }
+ error = 0;
+
+ if (ofproto) {
+ *ofproto = xport->xbridge->ofproto;
+ }
+
+exit:
+ if (fitnessp) {
+ *fitnessp = fitness;
+ }
+ ovs_rwlock_unlock(&xlate_rwlock);
+ return error;
+}
+
+static struct xbridge *
+xbridge_lookup(const struct ofproto_dpif *ofproto)
+{
+ struct xbridge *xbridge;
+
+ if (!ofproto) {
+ return NULL;
+ }
+
+ HMAP_FOR_EACH_IN_BUCKET (xbridge, hmap_node, hash_pointer(ofproto, 0),
+ &xbridges) {
+ if (xbridge->ofproto == ofproto) {
+ return xbridge;
+ }
+ }
+ return NULL;
+}
+
+static struct xbundle *
+xbundle_lookup(const struct ofbundle *ofbundle)
+{
+ struct xbundle *xbundle;
+
+ if (!ofbundle) {
+ return NULL;
+ }
+
+ HMAP_FOR_EACH_IN_BUCKET (xbundle, hmap_node, hash_pointer(ofbundle, 0),
+ &xbundles) {
+ if (xbundle->ofbundle == ofbundle) {
+ return xbundle;
+ }
+ }
+ return NULL;
+}
+
+static struct xport *
+xport_lookup(const struct ofport_dpif *ofport)
+{
+ struct xport *xport;
+
+ if (!ofport) {
+ return NULL;
+ }
+
+ HMAP_FOR_EACH_IN_BUCKET (xport, hmap_node, hash_pointer(ofport, 0),
+ &xports) {
+ if (xport->ofport == ofport) {
+ return xport;
+ }
+ }
+ return NULL;
+}
+
+static struct stp_port *
+xport_get_stp_port(const struct xport *xport)
+{
+ return xport->xbridge->stp && xport->stp_port_no != -1
+ ? stp_get_port(xport->xbridge->stp, xport->stp_port_no)
+ : NULL;
+}
+
+static enum stp_state
+xport_stp_learn_state(const struct xport *xport)
+{
+ struct stp_port *sp = xport_get_stp_port(xport);
+ return stp_learn_in_state(sp ? stp_port_get_state(sp) : STP_DISABLED);
+}
+
+static bool
+xport_stp_forward_state(const struct xport *xport)
+{
+ struct stp_port *sp = xport_get_stp_port(xport);
+ return stp_forward_in_state(sp ? stp_port_get_state(sp) : STP_DISABLED);
+}
+
+/* Returns true if STP should process 'flow'. Sets fields in 'wc' that
+ * were used to make the determination.*/