forked from mirrors/linux
		
	switchdev: add support for fdb add/del/dump via switchdev_port_obj ops.
- introduce port fdb obj and generic switchdev_port_fdb_add/del/dump() - use switchdev_port_fdb_add/del/dump in rocker/team/bonding ndo ops. - add support for fdb obj in switchdev_port_obj_add/del/dump() - switch rocker to implement fdb ops via switchdev_ops v3: updated to sync with named union changes. Signed-off-by: Sridhar Samudrala <sridhar.samudrala@intel.com> Signed-off-by: Scott Feldman <sfeldma@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									5d48ef3e95
								
							
						
					
					
						commit
						45d4122ca7
					
				
					 5 changed files with 312 additions and 107 deletions
				
			
		| 
						 | 
					@ -4039,6 +4039,9 @@ static const struct net_device_ops bond_netdev_ops = {
 | 
				
			||||||
	.ndo_bridge_setlink	= switchdev_port_bridge_setlink,
 | 
						.ndo_bridge_setlink	= switchdev_port_bridge_setlink,
 | 
				
			||||||
	.ndo_bridge_getlink	= switchdev_port_bridge_getlink,
 | 
						.ndo_bridge_getlink	= switchdev_port_bridge_getlink,
 | 
				
			||||||
	.ndo_bridge_dellink	= switchdev_port_bridge_dellink,
 | 
						.ndo_bridge_dellink	= switchdev_port_bridge_dellink,
 | 
				
			||||||
 | 
						.ndo_fdb_add		= switchdev_port_fdb_add,
 | 
				
			||||||
 | 
						.ndo_fdb_del		= switchdev_port_fdb_del,
 | 
				
			||||||
 | 
						.ndo_fdb_dump		= switchdev_port_fdb_dump,
 | 
				
			||||||
	.ndo_features_check	= passthru_features_check,
 | 
						.ndo_features_check	= passthru_features_check,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4193,110 +4193,6 @@ static int rocker_port_vlan_rx_kill_vid(struct net_device *dev,
 | 
				
			||||||
				ROCKER_OP_FLAG_REMOVE, vid);
 | 
									ROCKER_OP_FLAG_REMOVE, vid);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int rocker_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 | 
					 | 
				
			||||||
			       struct net_device *dev,
 | 
					 | 
				
			||||||
			       const unsigned char *addr, u16 vid,
 | 
					 | 
				
			||||||
			       u16 nlm_flags)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct rocker_port *rocker_port = netdev_priv(dev);
 | 
					 | 
				
			||||||
	__be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, vid, NULL);
 | 
					 | 
				
			||||||
	int flags = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!rocker_port_is_bridged(rocker_port))
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return rocker_port_fdb(rocker_port, SWITCHDEV_TRANS_NONE,
 | 
					 | 
				
			||||||
			       addr, vlan_id, flags);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int rocker_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
 | 
					 | 
				
			||||||
			       struct net_device *dev,
 | 
					 | 
				
			||||||
			       const unsigned char *addr, u16 vid)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct rocker_port *rocker_port = netdev_priv(dev);
 | 
					 | 
				
			||||||
	__be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, vid, NULL);
 | 
					 | 
				
			||||||
	int flags = ROCKER_OP_FLAG_REMOVE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!rocker_port_is_bridged(rocker_port))
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return rocker_port_fdb(rocker_port, SWITCHDEV_TRANS_NONE,
 | 
					 | 
				
			||||||
			       addr, vlan_id, flags);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int rocker_fdb_fill_info(struct sk_buff *skb,
 | 
					 | 
				
			||||||
				struct rocker_port *rocker_port,
 | 
					 | 
				
			||||||
				const unsigned char *addr, u16 vid,
 | 
					 | 
				
			||||||
				u32 portid, u32 seq, int type,
 | 
					 | 
				
			||||||
				unsigned int flags)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct nlmsghdr *nlh;
 | 
					 | 
				
			||||||
	struct ndmsg *ndm;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags);
 | 
					 | 
				
			||||||
	if (!nlh)
 | 
					 | 
				
			||||||
		return -EMSGSIZE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ndm = nlmsg_data(nlh);
 | 
					 | 
				
			||||||
	ndm->ndm_family	 = AF_BRIDGE;
 | 
					 | 
				
			||||||
	ndm->ndm_pad1    = 0;
 | 
					 | 
				
			||||||
	ndm->ndm_pad2    = 0;
 | 
					 | 
				
			||||||
	ndm->ndm_flags	 = NTF_SELF;
 | 
					 | 
				
			||||||
	ndm->ndm_type	 = 0;
 | 
					 | 
				
			||||||
	ndm->ndm_ifindex = rocker_port->dev->ifindex;
 | 
					 | 
				
			||||||
	ndm->ndm_state   = NUD_REACHABLE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr))
 | 
					 | 
				
			||||||
		goto nla_put_failure;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (vid && nla_put_u16(skb, NDA_VLAN, vid))
 | 
					 | 
				
			||||||
		goto nla_put_failure;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	nlmsg_end(skb, nlh);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
nla_put_failure:
 | 
					 | 
				
			||||||
	nlmsg_cancel(skb, nlh);
 | 
					 | 
				
			||||||
	return -EMSGSIZE;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int rocker_port_fdb_dump(struct sk_buff *skb,
 | 
					 | 
				
			||||||
				struct netlink_callback *cb,
 | 
					 | 
				
			||||||
				struct net_device *dev,
 | 
					 | 
				
			||||||
				struct net_device *filter_dev,
 | 
					 | 
				
			||||||
				int idx)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct rocker_port *rocker_port = netdev_priv(dev);
 | 
					 | 
				
			||||||
	struct rocker *rocker = rocker_port->rocker;
 | 
					 | 
				
			||||||
	struct rocker_fdb_tbl_entry *found;
 | 
					 | 
				
			||||||
	struct hlist_node *tmp;
 | 
					 | 
				
			||||||
	int bkt;
 | 
					 | 
				
			||||||
	unsigned long lock_flags;
 | 
					 | 
				
			||||||
	const unsigned char *addr;
 | 
					 | 
				
			||||||
	u16 vid;
 | 
					 | 
				
			||||||
	int err;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags);
 | 
					 | 
				
			||||||
	hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) {
 | 
					 | 
				
			||||||
		if (found->key.pport != rocker_port->pport)
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		if (idx < cb->args[0])
 | 
					 | 
				
			||||||
			goto skip;
 | 
					 | 
				
			||||||
		addr = found->key.addr;
 | 
					 | 
				
			||||||
		vid = rocker_port_vlan_to_vid(rocker_port, found->key.vlan_id);
 | 
					 | 
				
			||||||
		err = rocker_fdb_fill_info(skb, rocker_port, addr, vid,
 | 
					 | 
				
			||||||
					   NETLINK_CB(cb->skb).portid,
 | 
					 | 
				
			||||||
					   cb->nlh->nlmsg_seq,
 | 
					 | 
				
			||||||
					   RTM_NEWNEIGH, NLM_F_MULTI);
 | 
					 | 
				
			||||||
		if (err < 0)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
skip:
 | 
					 | 
				
			||||||
		++idx;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags);
 | 
					 | 
				
			||||||
	return idx;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int rocker_port_get_phys_port_name(struct net_device *dev,
 | 
					static int rocker_port_get_phys_port_name(struct net_device *dev,
 | 
				
			||||||
					  char *buf, size_t len)
 | 
										  char *buf, size_t len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -4320,12 +4216,12 @@ static const struct net_device_ops rocker_port_netdev_ops = {
 | 
				
			||||||
	.ndo_set_mac_address		= rocker_port_set_mac_address,
 | 
						.ndo_set_mac_address		= rocker_port_set_mac_address,
 | 
				
			||||||
	.ndo_vlan_rx_add_vid		= rocker_port_vlan_rx_add_vid,
 | 
						.ndo_vlan_rx_add_vid		= rocker_port_vlan_rx_add_vid,
 | 
				
			||||||
	.ndo_vlan_rx_kill_vid		= rocker_port_vlan_rx_kill_vid,
 | 
						.ndo_vlan_rx_kill_vid		= rocker_port_vlan_rx_kill_vid,
 | 
				
			||||||
	.ndo_fdb_add			= rocker_port_fdb_add,
 | 
					 | 
				
			||||||
	.ndo_fdb_del			= rocker_port_fdb_del,
 | 
					 | 
				
			||||||
	.ndo_fdb_dump			= rocker_port_fdb_dump,
 | 
					 | 
				
			||||||
	.ndo_bridge_getlink		= switchdev_port_bridge_getlink,
 | 
						.ndo_bridge_getlink		= switchdev_port_bridge_getlink,
 | 
				
			||||||
	.ndo_bridge_setlink		= switchdev_port_bridge_setlink,
 | 
						.ndo_bridge_setlink		= switchdev_port_bridge_setlink,
 | 
				
			||||||
	.ndo_bridge_dellink		= switchdev_port_bridge_dellink,
 | 
						.ndo_bridge_dellink		= switchdev_port_bridge_dellink,
 | 
				
			||||||
 | 
						.ndo_fdb_add			= switchdev_port_fdb_add,
 | 
				
			||||||
 | 
						.ndo_fdb_del			= switchdev_port_fdb_del,
 | 
				
			||||||
 | 
						.ndo_fdb_dump			= switchdev_port_fdb_dump,
 | 
				
			||||||
	.ndo_get_phys_port_name		= rocker_port_get_phys_port_name,
 | 
						.ndo_get_phys_port_name		= rocker_port_get_phys_port_name,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4447,6 +4343,19 @@ static int rocker_port_vlans_add(struct rocker_port *rocker_port,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rocker_port_fdb_add(struct rocker_port *rocker_port,
 | 
				
			||||||
 | 
								       enum switchdev_trans trans,
 | 
				
			||||||
 | 
								       struct switchdev_obj_fdb *fdb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						__be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL);
 | 
				
			||||||
 | 
						int flags = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!rocker_port_is_bridged(rocker_port))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rocker_port_fdb(rocker_port, trans, fdb->addr, vlan_id, flags);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int rocker_port_obj_add(struct net_device *dev,
 | 
					static int rocker_port_obj_add(struct net_device *dev,
 | 
				
			||||||
			       struct switchdev_obj *obj)
 | 
								       struct switchdev_obj *obj)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -4476,6 +4385,9 @@ static int rocker_port_obj_add(struct net_device *dev,
 | 
				
			||||||
					   htonl(fib4->dst), fib4->dst_len,
 | 
										   htonl(fib4->dst), fib4->dst_len,
 | 
				
			||||||
					   fib4->fi, fib4->tb_id, 0);
 | 
										   fib4->fi, fib4->tb_id, 0);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case SWITCHDEV_OBJ_PORT_FDB:
 | 
				
			||||||
 | 
							err = rocker_port_fdb_add(rocker_port, obj->trans, &obj->u.fdb);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		err = -EOPNOTSUPP;
 | 
							err = -EOPNOTSUPP;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
| 
						 | 
					@ -4513,6 +4425,19 @@ static int rocker_port_vlans_del(struct rocker_port *rocker_port,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rocker_port_fdb_del(struct rocker_port *rocker_port,
 | 
				
			||||||
 | 
								       enum switchdev_trans trans,
 | 
				
			||||||
 | 
								       struct switchdev_obj_fdb *fdb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						__be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL);
 | 
				
			||||||
 | 
						int flags = ROCKER_OP_FLAG_REMOVE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!rocker_port_is_bridged(rocker_port))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rocker_port_fdb(rocker_port, trans, fdb->addr, vlan_id, flags);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int rocker_port_obj_del(struct net_device *dev,
 | 
					static int rocker_port_obj_del(struct net_device *dev,
 | 
				
			||||||
			       struct switchdev_obj *obj)
 | 
								       struct switchdev_obj *obj)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -4531,6 +4456,54 @@ static int rocker_port_obj_del(struct net_device *dev,
 | 
				
			||||||
					   fib4->fi, fib4->tb_id,
 | 
										   fib4->fi, fib4->tb_id,
 | 
				
			||||||
					   ROCKER_OP_FLAG_REMOVE);
 | 
										   ROCKER_OP_FLAG_REMOVE);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case SWITCHDEV_OBJ_PORT_FDB:
 | 
				
			||||||
 | 
							err = rocker_port_fdb_del(rocker_port, obj->trans, &obj->u.fdb);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							err = -EOPNOTSUPP;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rocker_port_fdb_dump(struct rocker_port *rocker_port,
 | 
				
			||||||
 | 
									struct switchdev_obj *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct rocker *rocker = rocker_port->rocker;
 | 
				
			||||||
 | 
						struct switchdev_obj_fdb *fdb = &obj->u.fdb;
 | 
				
			||||||
 | 
						struct rocker_fdb_tbl_entry *found;
 | 
				
			||||||
 | 
						struct hlist_node *tmp;
 | 
				
			||||||
 | 
						unsigned long lock_flags;
 | 
				
			||||||
 | 
						int bkt;
 | 
				
			||||||
 | 
						int err = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags);
 | 
				
			||||||
 | 
						hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) {
 | 
				
			||||||
 | 
							if (found->key.pport != rocker_port->pport)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							fdb->addr = found->key.addr;
 | 
				
			||||||
 | 
							fdb->vid = rocker_port_vlan_to_vid(rocker_port,
 | 
				
			||||||
 | 
											   found->key.vlan_id);
 | 
				
			||||||
 | 
							err = obj->cb(rocker_port->dev, obj);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rocker_port_obj_dump(struct net_device *dev,
 | 
				
			||||||
 | 
									struct switchdev_obj *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct rocker_port *rocker_port = netdev_priv(dev);
 | 
				
			||||||
 | 
						int err = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (obj->id) {
 | 
				
			||||||
 | 
						case SWITCHDEV_OBJ_PORT_FDB:
 | 
				
			||||||
 | 
							err = rocker_port_fdb_dump(rocker_port, obj);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		err = -EOPNOTSUPP;
 | 
							err = -EOPNOTSUPP;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
| 
						 | 
					@ -4544,6 +4517,7 @@ static const struct switchdev_ops rocker_port_switchdev_ops = {
 | 
				
			||||||
	.switchdev_port_attr_set	= rocker_port_attr_set,
 | 
						.switchdev_port_attr_set	= rocker_port_attr_set,
 | 
				
			||||||
	.switchdev_port_obj_add		= rocker_port_obj_add,
 | 
						.switchdev_port_obj_add		= rocker_port_obj_add,
 | 
				
			||||||
	.switchdev_port_obj_del		= rocker_port_obj_del,
 | 
						.switchdev_port_obj_del		= rocker_port_obj_del,
 | 
				
			||||||
 | 
						.switchdev_port_obj_dump	= rocker_port_obj_dump,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/********************
 | 
					/********************
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1980,6 +1980,9 @@ static const struct net_device_ops team_netdev_ops = {
 | 
				
			||||||
	.ndo_bridge_setlink	= switchdev_port_bridge_setlink,
 | 
						.ndo_bridge_setlink	= switchdev_port_bridge_setlink,
 | 
				
			||||||
	.ndo_bridge_getlink	= switchdev_port_bridge_getlink,
 | 
						.ndo_bridge_getlink	= switchdev_port_bridge_getlink,
 | 
				
			||||||
	.ndo_bridge_dellink	= switchdev_port_bridge_dellink,
 | 
						.ndo_bridge_dellink	= switchdev_port_bridge_dellink,
 | 
				
			||||||
 | 
						.ndo_fdb_add		= switchdev_port_fdb_add,
 | 
				
			||||||
 | 
						.ndo_fdb_del		= switchdev_port_fdb_del,
 | 
				
			||||||
 | 
						.ndo_fdb_dump		= switchdev_port_fdb_dump,
 | 
				
			||||||
	.ndo_features_check	= passthru_features_check,
 | 
						.ndo_features_check	= passthru_features_check,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,11 +47,13 @@ enum switchdev_obj_id {
 | 
				
			||||||
	SWITCHDEV_OBJ_UNDEFINED,
 | 
						SWITCHDEV_OBJ_UNDEFINED,
 | 
				
			||||||
	SWITCHDEV_OBJ_PORT_VLAN,
 | 
						SWITCHDEV_OBJ_PORT_VLAN,
 | 
				
			||||||
	SWITCHDEV_OBJ_IPV4_FIB,
 | 
						SWITCHDEV_OBJ_IPV4_FIB,
 | 
				
			||||||
 | 
						SWITCHDEV_OBJ_PORT_FDB,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct switchdev_obj {
 | 
					struct switchdev_obj {
 | 
				
			||||||
	enum switchdev_obj_id id;
 | 
						enum switchdev_obj_id id;
 | 
				
			||||||
	enum switchdev_trans trans;
 | 
						enum switchdev_trans trans;
 | 
				
			||||||
 | 
						int (*cb)(struct net_device *dev, struct switchdev_obj *obj);
 | 
				
			||||||
	union {
 | 
						union {
 | 
				
			||||||
		struct switchdev_obj_vlan {		/* PORT_VLAN */
 | 
							struct switchdev_obj_vlan {		/* PORT_VLAN */
 | 
				
			||||||
			u16 flags;
 | 
								u16 flags;
 | 
				
			||||||
| 
						 | 
					@ -67,6 +69,10 @@ struct switchdev_obj {
 | 
				
			||||||
			u32 nlflags;
 | 
								u32 nlflags;
 | 
				
			||||||
			u32 tb_id;
 | 
								u32 tb_id;
 | 
				
			||||||
		} ipv4_fib;
 | 
							} ipv4_fib;
 | 
				
			||||||
 | 
							struct switchdev_obj_fdb {		/* PORT_FDB */
 | 
				
			||||||
 | 
								const unsigned char *addr;
 | 
				
			||||||
 | 
								u16 vid;
 | 
				
			||||||
 | 
							} fdb;
 | 
				
			||||||
	} u;
 | 
						} u;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,6 +86,8 @@ struct switchdev_obj {
 | 
				
			||||||
 * @switchdev_port_obj_add: Add an object to port (see switchdev_obj).
 | 
					 * @switchdev_port_obj_add: Add an object to port (see switchdev_obj).
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @switchdev_port_obj_del: Delete an object from port (see switchdev_obj).
 | 
					 * @switchdev_port_obj_del: Delete an object from port (see switchdev_obj).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @switchdev_port_obj_dump: Dump port objects (see switchdev_obj).
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct switchdev_ops {
 | 
					struct switchdev_ops {
 | 
				
			||||||
	int	(*switchdev_port_attr_get)(struct net_device *dev,
 | 
						int	(*switchdev_port_attr_get)(struct net_device *dev,
 | 
				
			||||||
| 
						 | 
					@ -90,6 +98,8 @@ struct switchdev_ops {
 | 
				
			||||||
					  struct switchdev_obj *obj);
 | 
										  struct switchdev_obj *obj);
 | 
				
			||||||
	int	(*switchdev_port_obj_del)(struct net_device *dev,
 | 
						int	(*switchdev_port_obj_del)(struct net_device *dev,
 | 
				
			||||||
					  struct switchdev_obj *obj);
 | 
										  struct switchdev_obj *obj);
 | 
				
			||||||
 | 
						int	(*switchdev_port_obj_dump)(struct net_device *dev,
 | 
				
			||||||
 | 
										  struct switchdev_obj *obj);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum switchdev_notifier_type {
 | 
					enum switchdev_notifier_type {
 | 
				
			||||||
| 
						 | 
					@ -121,6 +131,7 @@ int switchdev_port_attr_set(struct net_device *dev,
 | 
				
			||||||
			    struct switchdev_attr *attr);
 | 
								    struct switchdev_attr *attr);
 | 
				
			||||||
int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj);
 | 
					int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj);
 | 
				
			||||||
int switchdev_port_obj_del(struct net_device *dev, struct switchdev_obj *obj);
 | 
					int switchdev_port_obj_del(struct net_device *dev, struct switchdev_obj *obj);
 | 
				
			||||||
 | 
					int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj);
 | 
				
			||||||
int register_switchdev_notifier(struct notifier_block *nb);
 | 
					int register_switchdev_notifier(struct notifier_block *nb);
 | 
				
			||||||
int unregister_switchdev_notifier(struct notifier_block *nb);
 | 
					int unregister_switchdev_notifier(struct notifier_block *nb);
 | 
				
			||||||
int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
 | 
					int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
 | 
				
			||||||
| 
						 | 
					@ -137,6 +148,15 @@ int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
 | 
				
			||||||
int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
 | 
					int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
 | 
				
			||||||
			   u8 tos, u8 type, u32 tb_id);
 | 
								   u8 tos, u8 type, u32 tb_id);
 | 
				
			||||||
void switchdev_fib_ipv4_abort(struct fib_info *fi);
 | 
					void switchdev_fib_ipv4_abort(struct fib_info *fi);
 | 
				
			||||||
 | 
					int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 | 
				
			||||||
 | 
								   struct net_device *dev, const unsigned char *addr,
 | 
				
			||||||
 | 
								   u16 vid, u16 nlm_flags);
 | 
				
			||||||
 | 
					int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
 | 
				
			||||||
 | 
								   struct net_device *dev, const unsigned char *addr,
 | 
				
			||||||
 | 
								   u16 vid);
 | 
				
			||||||
 | 
					int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
 | 
				
			||||||
 | 
								    struct net_device *dev,
 | 
				
			||||||
 | 
								    struct net_device *filter_dev, int idx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -164,6 +184,12 @@ static inline int switchdev_port_obj_del(struct net_device *dev,
 | 
				
			||||||
	return -EOPNOTSUPP;
 | 
						return -EOPNOTSUPP;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int switchdev_port_obj_dump(struct net_device *dev,
 | 
				
			||||||
 | 
										  struct switchdev_obj *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline int register_switchdev_notifier(struct notifier_block *nb)
 | 
					static inline int register_switchdev_notifier(struct notifier_block *nb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					@ -221,6 +247,30 @@ static inline void switchdev_fib_ipv4_abort(struct fib_info *fi)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 | 
				
			||||||
 | 
										 struct net_device *dev,
 | 
				
			||||||
 | 
										 const unsigned char *addr,
 | 
				
			||||||
 | 
										 u16 vid, u16 nlm_flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
 | 
				
			||||||
 | 
										 struct net_device *dev,
 | 
				
			||||||
 | 
										 const unsigned char *addr, u16 vid)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int switchdev_port_fdb_dump(struct sk_buff *skb,
 | 
				
			||||||
 | 
										  struct netlink_callback *cb,
 | 
				
			||||||
 | 
										  struct net_device *dev,
 | 
				
			||||||
 | 
										  struct net_device *filter_dev,
 | 
				
			||||||
 | 
										  int idx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* _LINUX_SWITCHDEV_H_ */
 | 
					#endif /* _LINUX_SWITCHDEV_H_ */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -296,6 +296,36 @@ int switchdev_port_obj_del(struct net_device *dev, struct switchdev_obj *obj)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
 | 
					EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *	switchdev_port_obj_dump - Dump port objects
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	@dev: port device
 | 
				
			||||||
 | 
					 *	@obj: object to dump
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct switchdev_ops *ops = dev->switchdev_ops;
 | 
				
			||||||
 | 
						struct net_device *lower_dev;
 | 
				
			||||||
 | 
						struct list_head *iter;
 | 
				
			||||||
 | 
						int err = -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ops && ops->switchdev_port_obj_dump)
 | 
				
			||||||
 | 
							return ops->switchdev_port_obj_dump(dev, obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Switch device port(s) may be stacked under
 | 
				
			||||||
 | 
						 * bond/team/vlan dev, so recurse down to dump objects on
 | 
				
			||||||
 | 
						 * first port at bottom of stack.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						netdev_for_each_lower_dev(dev, lower_dev, iter) {
 | 
				
			||||||
 | 
							err = switchdev_port_obj_dump(lower_dev, obj);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(switchdev_port_obj_dump);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static DEFINE_MUTEX(switchdev_mutex);
 | 
					static DEFINE_MUTEX(switchdev_mutex);
 | 
				
			||||||
static RAW_NOTIFIER_HEAD(switchdev_notif_chain);
 | 
					static RAW_NOTIFIER_HEAD(switchdev_notif_chain);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -566,6 +596,151 @@ int switchdev_port_bridge_dellink(struct net_device *dev,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(switchdev_port_bridge_dellink);
 | 
					EXPORT_SYMBOL_GPL(switchdev_port_bridge_dellink);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *	switchdev_port_fdb_add - Add FDB (MAC/VLAN) entry to port
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	@ndmsg: netlink hdr
 | 
				
			||||||
 | 
					 *	@nlattr: netlink attributes
 | 
				
			||||||
 | 
					 *	@dev: port device
 | 
				
			||||||
 | 
					 *	@addr: MAC address to add
 | 
				
			||||||
 | 
					 *	@vid: VLAN to add
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	Add FDB entry to switch device.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 | 
				
			||||||
 | 
								   struct net_device *dev, const unsigned char *addr,
 | 
				
			||||||
 | 
								   u16 vid, u16 nlm_flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct switchdev_obj obj = {
 | 
				
			||||||
 | 
							.id = SWITCHDEV_OBJ_PORT_FDB,
 | 
				
			||||||
 | 
							.u.fdb = {
 | 
				
			||||||
 | 
								.addr = addr,
 | 
				
			||||||
 | 
								.vid = vid,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return switchdev_port_obj_add(dev, &obj);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(switchdev_port_fdb_add);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *	switchdev_port_fdb_del - Delete FDB (MAC/VLAN) entry from port
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	@ndmsg: netlink hdr
 | 
				
			||||||
 | 
					 *	@nlattr: netlink attributes
 | 
				
			||||||
 | 
					 *	@dev: port device
 | 
				
			||||||
 | 
					 *	@addr: MAC address to delete
 | 
				
			||||||
 | 
					 *	@vid: VLAN to delete
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	Delete FDB entry from switch device.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
 | 
				
			||||||
 | 
								   struct net_device *dev, const unsigned char *addr,
 | 
				
			||||||
 | 
								   u16 vid)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct switchdev_obj obj = {
 | 
				
			||||||
 | 
							.id = SWITCHDEV_OBJ_PORT_FDB,
 | 
				
			||||||
 | 
							.u.fdb = {
 | 
				
			||||||
 | 
								.addr = addr,
 | 
				
			||||||
 | 
								.vid = vid,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return switchdev_port_obj_del(dev, &obj);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(switchdev_port_fdb_del);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct switchdev_fdb_dump {
 | 
				
			||||||
 | 
						struct switchdev_obj obj;
 | 
				
			||||||
 | 
						struct sk_buff *skb;
 | 
				
			||||||
 | 
						struct netlink_callback *cb;
 | 
				
			||||||
 | 
						struct net_device *filter_dev;
 | 
				
			||||||
 | 
						int idx;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int switchdev_port_fdb_dump_cb(struct net_device *dev,
 | 
				
			||||||
 | 
									      struct switchdev_obj *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct switchdev_fdb_dump *dump =
 | 
				
			||||||
 | 
							container_of(obj, struct switchdev_fdb_dump, obj);
 | 
				
			||||||
 | 
						u32 portid = NETLINK_CB(dump->cb->skb).portid;
 | 
				
			||||||
 | 
						u32 seq = dump->cb->nlh->nlmsg_seq;
 | 
				
			||||||
 | 
						struct nlmsghdr *nlh;
 | 
				
			||||||
 | 
						struct ndmsg *ndm;
 | 
				
			||||||
 | 
						struct net_device *master = netdev_master_upper_dev_get(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dump->idx < dump->cb->args[0])
 | 
				
			||||||
 | 
							goto skip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (master && dump->filter_dev != master)
 | 
				
			||||||
 | 
							goto skip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
 | 
				
			||||||
 | 
								sizeof(*ndm), NLM_F_MULTI);
 | 
				
			||||||
 | 
						if (!nlh)
 | 
				
			||||||
 | 
							return -EMSGSIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ndm = nlmsg_data(nlh);
 | 
				
			||||||
 | 
						ndm->ndm_family  = AF_BRIDGE;
 | 
				
			||||||
 | 
						ndm->ndm_pad1    = 0;
 | 
				
			||||||
 | 
						ndm->ndm_pad2    = 0;
 | 
				
			||||||
 | 
						ndm->ndm_flags   = NTF_SELF;
 | 
				
			||||||
 | 
						ndm->ndm_type    = 0;
 | 
				
			||||||
 | 
						ndm->ndm_ifindex = dev->ifindex;
 | 
				
			||||||
 | 
						ndm->ndm_state   = NUD_REACHABLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, obj->u.fdb.addr))
 | 
				
			||||||
 | 
							goto nla_put_failure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (obj->u.fdb.vid && nla_put_u16(dump->skb, NDA_VLAN, obj->u.fdb.vid))
 | 
				
			||||||
 | 
							goto nla_put_failure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nlmsg_end(dump->skb, nlh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					skip:
 | 
				
			||||||
 | 
						dump->idx++;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nla_put_failure:
 | 
				
			||||||
 | 
						nlmsg_cancel(dump->skb, nlh);
 | 
				
			||||||
 | 
						return -EMSGSIZE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *	switchdev_port_fdb_dump - Dump port FDB (MAC/VLAN) entries
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	@skb: netlink skb
 | 
				
			||||||
 | 
					 *	@cb: netlink callback
 | 
				
			||||||
 | 
					 *	@dev: port device
 | 
				
			||||||
 | 
					 *	@filter_dev: filter device
 | 
				
			||||||
 | 
					 *	@idx:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	Delete FDB entry from switch device.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
 | 
				
			||||||
 | 
								    struct net_device *dev,
 | 
				
			||||||
 | 
								    struct net_device *filter_dev, int idx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct switchdev_fdb_dump dump = {
 | 
				
			||||||
 | 
							.obj = {
 | 
				
			||||||
 | 
								.id = SWITCHDEV_OBJ_PORT_FDB,
 | 
				
			||||||
 | 
								.cb = switchdev_port_fdb_dump_cb,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.skb = skb,
 | 
				
			||||||
 | 
							.cb = cb,
 | 
				
			||||||
 | 
							.filter_dev = filter_dev,
 | 
				
			||||||
 | 
							.idx = idx,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = switchdev_port_obj_dump(dev, &dump.obj);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return dump.idx;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct net_device *switchdev_get_lowest_dev(struct net_device *dev)
 | 
					static struct net_device *switchdev_get_lowest_dev(struct net_device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const struct switchdev_ops *ops = dev->switchdev_ops;
 | 
						const struct switchdev_ops *ops = dev->switchdev_ops;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue