net/bridge/br_switchdev.c

Source file repositories/reference/linux-study-clean/net/bridge/br_switchdev.c

File Facts

System
Linux kernel
Corpus path
net/bridge/br_switchdev.c
Extension
.c
Size
22153 bytes
Lines
891
Domain
Networking Core
Bucket
Sockets, Protocols, Packet Path, And Network Policy
Inferred role
Networking Core: implementation source
Status
source implementation candidate

Why This File Exists

Networking stack implementation surface: socket APIs, protocol dispatch, packet flow, routing, filtering, and network namespaces.

Dependency Surface

Detected Declarations

Annotated Snippet

struct br_switchdev_mdb_complete_info {
	struct net_bridge_port *port;
	struct br_ip ip;
};

static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *priv)
{
	struct br_switchdev_mdb_complete_info *data = priv;
	struct net_bridge_port_group __rcu **pp;
	struct net_bridge_port_group *p;
	struct net_bridge_mdb_entry *mp;
	struct net_bridge_port *port = data->port;
	struct net_bridge *br = port->br;
	u8 old_flags;

	if (err == -EOPNOTSUPP)
		goto out_free;

	spin_lock_bh(&br->multicast_lock);
	mp = br_mdb_ip_get(br, &data->ip);
	if (!mp)
		goto out;
	for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
	     pp = &p->next) {
		if (p->key.port != port)
			continue;

		old_flags = p->flags;
		br_multicast_set_pg_offload_flags(p, !err);
		if (br_mdb_should_notify(br, old_flags ^ p->flags))
			br_mdb_flag_change_notify(br->dev, mp, p);
	}
out:
	spin_unlock_bh(&br->multicast_lock);
out_free:
	kfree(priv);
}

static void br_switchdev_mdb_populate(struct switchdev_obj_port_mdb *mdb,
				      const struct net_bridge_mdb_entry *mp)
{
	if (mp->addr.proto == htons(ETH_P_IP))
		ip_eth_mc_map(mp->addr.dst.ip4, mdb->addr);
#if IS_ENABLED(CONFIG_IPV6)
	else if (mp->addr.proto == htons(ETH_P_IPV6))
		ipv6_eth_mc_map(&mp->addr.dst.ip6, mdb->addr);
#endif
	else
		ether_addr_copy(mdb->addr, mp->addr.dst.mac_addr);

	mdb->vid = mp->addr.vid;
}

static void br_switchdev_host_mdb_one(struct net_device *dev,
				      struct net_device *lower_dev,
				      struct net_bridge_mdb_entry *mp,
				      int type)
{
	struct switchdev_obj_port_mdb mdb = {
		.obj = {
			.id = SWITCHDEV_OBJ_ID_HOST_MDB,
			.flags = SWITCHDEV_F_DEFER,
			.orig_dev = dev,
		},
	};

	br_switchdev_mdb_populate(&mdb, mp);

	switch (type) {
	case RTM_NEWMDB:
		switchdev_port_obj_add(lower_dev, &mdb.obj, NULL);
		break;
	case RTM_DELMDB:
		switchdev_port_obj_del(lower_dev, &mdb.obj);
		break;
	}
}

static void br_switchdev_host_mdb(struct net_device *dev,
				  struct net_bridge_mdb_entry *mp, int type)
{
	struct net_device *lower_dev;
	struct list_head *iter;

	netdev_for_each_lower_dev(dev, lower_dev, iter)
		br_switchdev_host_mdb_one(dev, lower_dev, mp, type);
}

static int
br_switchdev_mdb_replay_one(struct notifier_block *nb, struct net_device *dev,

Annotation

Implementation Notes