+static struct mac_learning_port *
+mac_learning_port_lookup(struct mac_learning *ml, void *port)
+{
+ struct mac_learning_port *mlport;
+
+ HMAP_FOR_EACH_IN_BUCKET (mlport, hmap_node, hash_pointer(port, ml->secret),
+ &ml->ports_by_ptr) {
+ if (mlport->port == port) {
+ return mlport;
+ }
+ }
+ return NULL;
+}
+
+/* Changes the client-owned pointer for entry 'e' in 'ml' to 'port'. The
+ * pointer can be retrieved with mac_entry_get_port().
+ *
+ * The MAC-learning implementation treats the data that 'port' points to as
+ * opaque and never tries to dereference it. However, when a MAC learning
+ * table becomes overfull, so that eviction is required, the implementation
+ * does first evict MAC entries for the most common 'port's values in 'ml', so
+ * that there is a degree of fairness, that is, each port is entitled to its
+ * fair share of MAC entries. */
+void
+mac_entry_set_port(struct mac_learning *ml, struct mac_entry *e, void *port)
+ OVS_REQ_WRLOCK(ml->rwlock)
+{
+ if (mac_entry_get_port(ml, e) != port) {
+ ml->need_revalidate = true;
+
+ if (e->mlport) {
+ struct mac_learning_port *mlport = e->mlport;
+ list_remove(&e->port_lru_node);
+
+ if (list_is_empty(&mlport->port_lrus)) {
+ ovs_assert(mlport->heap_node.priority == 1);
+ hmap_remove(&ml->ports_by_ptr, &mlport->hmap_node);
+ heap_remove(&ml->ports_by_usage, &mlport->heap_node);
+ free(mlport);
+ } else {
+ ovs_assert(mlport->heap_node.priority > 1);
+ heap_change(&ml->ports_by_usage, &mlport->heap_node,
+ mlport->heap_node.priority - 1);
+ }
+ e->mlport = NULL;
+ }
+
+ if (port) {
+ struct mac_learning_port *mlport;
+
+ mlport = mac_learning_port_lookup(ml, port);
+ if (!mlport) {
+ mlport = xzalloc(sizeof *mlport);
+ hmap_insert(&ml->ports_by_ptr, &mlport->hmap_node,
+ hash_pointer(port, ml->secret));
+ heap_insert(&ml->ports_by_usage, &mlport->heap_node, 1);
+ mlport->port = port;
+ list_init(&mlport->port_lrus);
+ } else {
+ heap_change(&ml->ports_by_usage, &mlport->heap_node,
+ mlport->heap_node.priority + 1);
+ }
+ list_push_back(&mlport->port_lrus, &e->port_lru_node);
+ e->mlport = mlport;
+ }
+ }
+}
+
+/* Finds one of the ports with the most MAC entries and evicts its least
+ * recently used entry. */
+static void
+evict_mac_entry_fairly(struct mac_learning *ml)
+ OVS_REQ_WRLOCK(ml->rwlock)
+{
+ struct mac_learning_port *mlport;
+ struct mac_entry *e;
+
+ mlport = CONTAINER_OF(heap_max(&ml->ports_by_usage),
+ struct mac_learning_port, heap_node);
+ e = CONTAINER_OF(list_front(&mlport->port_lrus),
+ struct mac_entry, port_lru_node);
+ mac_learning_expire(ml, e);
+}
+