forked from mirrors/linux
		
	Use more inclusive terms throughout the DSA subsystem by moving away from "master" which is replaced by "conduit" and "slave" which is replaced by "user". No functional changes. Acked-by: Rob Herring <robh@kernel.org> Acked-by: Stephen Hemminger <stephen@networkplumber.org> Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com> Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com> Link: https://lore.kernel.org/r/20231023181729.1191071-2-florian.fainelli@broadcom.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
		
			
				
	
	
		
			310 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			310 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
						|
 | 
						|
#ifndef __DSA_TAG_H
 | 
						|
#define __DSA_TAG_H
 | 
						|
 | 
						|
#include <linux/if_vlan.h>
 | 
						|
#include <linux/list.h>
 | 
						|
#include <linux/types.h>
 | 
						|
#include <net/dsa.h>
 | 
						|
 | 
						|
#include "port.h"
 | 
						|
#include "user.h"
 | 
						|
 | 
						|
struct dsa_tag_driver {
 | 
						|
	const struct dsa_device_ops *ops;
 | 
						|
	struct list_head list;
 | 
						|
	struct module *owner;
 | 
						|
};
 | 
						|
 | 
						|
extern struct packet_type dsa_pack_type;
 | 
						|
 | 
						|
const struct dsa_device_ops *dsa_tag_driver_get_by_id(int tag_protocol);
 | 
						|
const struct dsa_device_ops *dsa_tag_driver_get_by_name(const char *name);
 | 
						|
void dsa_tag_driver_put(const struct dsa_device_ops *ops);
 | 
						|
const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops);
 | 
						|
 | 
						|
static inline int dsa_tag_protocol_overhead(const struct dsa_device_ops *ops)
 | 
						|
{
 | 
						|
	return ops->needed_headroom + ops->needed_tailroom;
 | 
						|
}
 | 
						|
 | 
						|
static inline struct net_device *dsa_conduit_find_user(struct net_device *dev,
 | 
						|
						       int device, int port)
 | 
						|
{
 | 
						|
	struct dsa_port *cpu_dp = dev->dsa_ptr;
 | 
						|
	struct dsa_switch_tree *dst = cpu_dp->dst;
 | 
						|
	struct dsa_port *dp;
 | 
						|
 | 
						|
	list_for_each_entry(dp, &dst->ports, list)
 | 
						|
		if (dp->ds->index == device && dp->index == port &&
 | 
						|
		    dp->type == DSA_PORT_TYPE_USER)
 | 
						|
			return dp->user;
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* If under a bridge with vlan_filtering=0, make sure to send pvid-tagged
 | 
						|
 * frames as untagged, since the bridge will not untag them.
 | 
						|
 */
 | 
						|
static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb)
 | 
						|
{
 | 
						|
	struct dsa_port *dp = dsa_user_to_port(skb->dev);
 | 
						|
	struct net_device *br = dsa_port_bridge_dev_get(dp);
 | 
						|
	struct net_device *dev = skb->dev;
 | 
						|
	struct net_device *upper_dev;
 | 
						|
	u16 vid, pvid, proto;
 | 
						|
	int err;
 | 
						|
 | 
						|
	if (!br || br_vlan_enabled(br))
 | 
						|
		return skb;
 | 
						|
 | 
						|
	err = br_vlan_get_proto(br, &proto);
 | 
						|
	if (err)
 | 
						|
		return skb;
 | 
						|
 | 
						|
	/* Move VLAN tag from data to hwaccel */
 | 
						|
	if (!skb_vlan_tag_present(skb) && skb->protocol == htons(proto)) {
 | 
						|
		skb = skb_vlan_untag(skb);
 | 
						|
		if (!skb)
 | 
						|
			return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!skb_vlan_tag_present(skb))
 | 
						|
		return skb;
 | 
						|
 | 
						|
	vid = skb_vlan_tag_get_id(skb);
 | 
						|
 | 
						|
	/* We already run under an RCU read-side critical section since
 | 
						|
	 * we are called from netif_receive_skb_list_internal().
 | 
						|
	 */
 | 
						|
	err = br_vlan_get_pvid_rcu(dev, &pvid);
 | 
						|
	if (err)
 | 
						|
		return skb;
 | 
						|
 | 
						|
	if (vid != pvid)
 | 
						|
		return skb;
 | 
						|
 | 
						|
	/* The sad part about attempting to untag from DSA is that we
 | 
						|
	 * don't know, unless we check, if the skb will end up in
 | 
						|
	 * the bridge's data path - br_allowed_ingress() - or not.
 | 
						|
	 * For example, there might be an 8021q upper for the
 | 
						|
	 * default_pvid of the bridge, which will steal VLAN-tagged traffic
 | 
						|
	 * from the bridge's data path. This is a configuration that DSA
 | 
						|
	 * supports because vlan_filtering is 0. In that case, we should
 | 
						|
	 * definitely keep the tag, to make sure it keeps working.
 | 
						|
	 */
 | 
						|
	upper_dev = __vlan_find_dev_deep_rcu(br, htons(proto), vid);
 | 
						|
	if (upper_dev)
 | 
						|
		return skb;
 | 
						|
 | 
						|
	__vlan_hwaccel_clear_tag(skb);
 | 
						|
 | 
						|
	return skb;
 | 
						|
}
 | 
						|
 | 
						|
/* For switches without hardware support for DSA tagging to be able
 | 
						|
 * to support termination through the bridge.
 | 
						|
 */
 | 
						|
static inline struct net_device *
 | 
						|
dsa_find_designated_bridge_port_by_vid(struct net_device *conduit, u16 vid)
 | 
						|
{
 | 
						|
	struct dsa_port *cpu_dp = conduit->dsa_ptr;
 | 
						|
	struct dsa_switch_tree *dst = cpu_dp->dst;
 | 
						|
	struct bridge_vlan_info vinfo;
 | 
						|
	struct net_device *user;
 | 
						|
	struct dsa_port *dp;
 | 
						|
	int err;
 | 
						|
 | 
						|
	list_for_each_entry(dp, &dst->ports, list) {
 | 
						|
		if (dp->type != DSA_PORT_TYPE_USER)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (!dp->bridge)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (dp->stp_state != BR_STATE_LEARNING &&
 | 
						|
		    dp->stp_state != BR_STATE_FORWARDING)
 | 
						|
			continue;
 | 
						|
 | 
						|
		/* Since the bridge might learn this packet, keep the CPU port
 | 
						|
		 * affinity with the port that will be used for the reply on
 | 
						|
		 * xmit.
 | 
						|
		 */
 | 
						|
		if (dp->cpu_dp != cpu_dp)
 | 
						|
			continue;
 | 
						|
 | 
						|
		user = dp->user;
 | 
						|
 | 
						|
		err = br_vlan_get_info_rcu(user, vid, &vinfo);
 | 
						|
		if (err)
 | 
						|
			continue;
 | 
						|
 | 
						|
		return user;
 | 
						|
	}
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* If the ingress port offloads the bridge, we mark the frame as autonomously
 | 
						|
 * forwarded by hardware, so the software bridge doesn't forward in twice, back
 | 
						|
 * to us, because we already did. However, if we're in fallback mode and we do
 | 
						|
 * software bridging, we are not offloading it, therefore the dp->bridge
 | 
						|
 * pointer is not populated, and flooding needs to be done by software (we are
 | 
						|
 * effectively operating in standalone ports mode).
 | 
						|
 */
 | 
						|
static inline void dsa_default_offload_fwd_mark(struct sk_buff *skb)
 | 
						|
{
 | 
						|
	struct dsa_port *dp = dsa_user_to_port(skb->dev);
 | 
						|
 | 
						|
	skb->offload_fwd_mark = !!(dp->bridge);
 | 
						|
}
 | 
						|
 | 
						|
/* Helper for removing DSA header tags from packets in the RX path.
 | 
						|
 * Must not be called before skb_pull(len).
 | 
						|
 *                                                                 skb->data
 | 
						|
 *                                                                         |
 | 
						|
 *                                                                         v
 | 
						|
 * |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
 | 
						|
 * +-----------------------+-----------------------+---------------+-------+
 | 
						|
 * |    Destination MAC    |      Source MAC       |  DSA header   | EType |
 | 
						|
 * +-----------------------+-----------------------+---------------+-------+
 | 
						|
 *                                                 |               |
 | 
						|
 * <----- len ----->                               <----- len ----->
 | 
						|
 *                 |
 | 
						|
 *       >>>>>>>   v
 | 
						|
 *       >>>>>>>   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
 | 
						|
 *       >>>>>>>   +-----------------------+-----------------------+-------+
 | 
						|
 *       >>>>>>>   |    Destination MAC    |      Source MAC       | EType |
 | 
						|
 *                 +-----------------------+-----------------------+-------+
 | 
						|
 *                                                                         ^
 | 
						|
 *                                                                         |
 | 
						|
 *                                                                 skb->data
 | 
						|
 */
 | 
						|
static inline void dsa_strip_etype_header(struct sk_buff *skb, int len)
 | 
						|
{
 | 
						|
	memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - len, 2 * ETH_ALEN);
 | 
						|
}
 | 
						|
 | 
						|
/* Helper for creating space for DSA header tags in TX path packets.
 | 
						|
 * Must not be called before skb_push(len).
 | 
						|
 *
 | 
						|
 * Before:
 | 
						|
 *
 | 
						|
 *       <<<<<<<   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
 | 
						|
 * ^     <<<<<<<   +-----------------------+-----------------------+-------+
 | 
						|
 * |     <<<<<<<   |    Destination MAC    |      Source MAC       | EType |
 | 
						|
 * |               +-----------------------+-----------------------+-------+
 | 
						|
 * <----- len ----->
 | 
						|
 * |
 | 
						|
 * |
 | 
						|
 * skb->data
 | 
						|
 *
 | 
						|
 * After:
 | 
						|
 *
 | 
						|
 * |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
 | 
						|
 * +-----------------------+-----------------------+---------------+-------+
 | 
						|
 * |    Destination MAC    |      Source MAC       |  DSA header   | EType |
 | 
						|
 * +-----------------------+-----------------------+---------------+-------+
 | 
						|
 * ^                                               |               |
 | 
						|
 * |                                               <----- len ----->
 | 
						|
 * skb->data
 | 
						|
 */
 | 
						|
static inline void dsa_alloc_etype_header(struct sk_buff *skb, int len)
 | 
						|
{
 | 
						|
	memmove(skb->data, skb->data + len, 2 * ETH_ALEN);
 | 
						|
}
 | 
						|
 | 
						|
/* On RX, eth_type_trans() on the DSA conduit pulls ETH_HLEN bytes starting from
 | 
						|
 * skb_mac_header(skb), which leaves skb->data pointing at the first byte after
 | 
						|
 * what the DSA conduit perceives as the EtherType (the beginning of the L3
 | 
						|
 * protocol). Since DSA EtherType header taggers treat the EtherType as part of
 | 
						|
 * the DSA tag itself, and the EtherType is 2 bytes in length, the DSA header
 | 
						|
 * is located 2 bytes behind skb->data. Note that EtherType in this context
 | 
						|
 * means the first 2 bytes of the DSA header, not the encapsulated EtherType
 | 
						|
 * that will become visible after the DSA header is stripped.
 | 
						|
 */
 | 
						|
static inline void *dsa_etype_header_pos_rx(struct sk_buff *skb)
 | 
						|
{
 | 
						|
	return skb->data - 2;
 | 
						|
}
 | 
						|
 | 
						|
/* On TX, skb->data points to the MAC header, which means that EtherType
 | 
						|
 * header taggers start exactly where the EtherType is (the EtherType is
 | 
						|
 * treated as part of the DSA header).
 | 
						|
 */
 | 
						|
static inline void *dsa_etype_header_pos_tx(struct sk_buff *skb)
 | 
						|
{
 | 
						|
	return skb->data + 2 * ETH_ALEN;
 | 
						|
}
 | 
						|
 | 
						|
/* Create 2 modaliases per tagging protocol, one to auto-load the module
 | 
						|
 * given the ID reported by get_tag_protocol(), and the other by name.
 | 
						|
 */
 | 
						|
#define DSA_TAG_DRIVER_ALIAS "dsa_tag:"
 | 
						|
#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto, __name) \
 | 
						|
	MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __name); \
 | 
						|
	MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS "id-" \
 | 
						|
		     __stringify(__proto##_VALUE))
 | 
						|
 | 
						|
void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[],
 | 
						|
			      unsigned int count,
 | 
						|
			      struct module *owner);
 | 
						|
void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[],
 | 
						|
				unsigned int count);
 | 
						|
 | 
						|
#define dsa_tag_driver_module_drivers(__dsa_tag_drivers_array, __count)	\
 | 
						|
static int __init dsa_tag_driver_module_init(void)			\
 | 
						|
{									\
 | 
						|
	dsa_tag_drivers_register(__dsa_tag_drivers_array, __count,	\
 | 
						|
				 THIS_MODULE);				\
 | 
						|
	return 0;							\
 | 
						|
}									\
 | 
						|
module_init(dsa_tag_driver_module_init);				\
 | 
						|
									\
 | 
						|
static void __exit dsa_tag_driver_module_exit(void)			\
 | 
						|
{									\
 | 
						|
	dsa_tag_drivers_unregister(__dsa_tag_drivers_array, __count);	\
 | 
						|
}									\
 | 
						|
module_exit(dsa_tag_driver_module_exit)
 | 
						|
 | 
						|
/**
 | 
						|
 * module_dsa_tag_drivers() - Helper macro for registering DSA tag
 | 
						|
 * drivers
 | 
						|
 * @__ops_array: Array of tag driver structures
 | 
						|
 *
 | 
						|
 * Helper macro for DSA tag drivers which do not do anything special
 | 
						|
 * in module init/exit. Each module may only use this macro once, and
 | 
						|
 * calling it replaces module_init() and module_exit().
 | 
						|
 */
 | 
						|
#define module_dsa_tag_drivers(__ops_array)				\
 | 
						|
dsa_tag_driver_module_drivers(__ops_array, ARRAY_SIZE(__ops_array))
 | 
						|
 | 
						|
#define DSA_TAG_DRIVER_NAME(__ops) dsa_tag_driver ## _ ## __ops
 | 
						|
 | 
						|
/* Create a static structure we can build a linked list of dsa_tag
 | 
						|
 * drivers
 | 
						|
 */
 | 
						|
#define DSA_TAG_DRIVER(__ops)						\
 | 
						|
static struct dsa_tag_driver DSA_TAG_DRIVER_NAME(__ops) = {		\
 | 
						|
	.ops = &__ops,							\
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * module_dsa_tag_driver() - Helper macro for registering a single DSA tag
 | 
						|
 * driver
 | 
						|
 * @__ops: Single tag driver structures
 | 
						|
 *
 | 
						|
 * Helper macro for DSA tag drivers which do not do anything special
 | 
						|
 * in module init/exit. Each module may only use this macro once, and
 | 
						|
 * calling it replaces module_init() and module_exit().
 | 
						|
 */
 | 
						|
#define module_dsa_tag_driver(__ops)					\
 | 
						|
DSA_TAG_DRIVER(__ops);							\
 | 
						|
									\
 | 
						|
static struct dsa_tag_driver *dsa_tag_driver_array[] =	{		\
 | 
						|
	&DSA_TAG_DRIVER_NAME(__ops)					\
 | 
						|
};									\
 | 
						|
module_dsa_tag_drivers(dsa_tag_driver_array)
 | 
						|
 | 
						|
#endif
 |