mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	net: dsa: add support for offloading HSR
Add support for offloading of HSR/PRP (IEC 62439-3) tag insertion
tag removal, duplicate generation and forwarding on DSA switches.
Add DSA_NOTIFIER_HSR_JOIN and DSA_NOTIFIER_HSR_LEAVE which trigger calls
to .port_hsr_join and .port_hsr_leave in the DSA driver for the switch.
The DSA switch driver should then set netdev feature flags for the
HSR/PRP operation that it offloads.
    NETIF_F_HW_HSR_TAG_INS
    NETIF_F_HW_HSR_TAG_RM
    NETIF_F_HW_HSR_FWD
    NETIF_F_HW_HSR_DUP
Signed-off-by: George McCollister <george.mccollister@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									dcf0cd1cc5
								
							
						
					
					
						commit
						18596f504a
					
				
					 5 changed files with 96 additions and 0 deletions
				
			
		| 
						 | 
					@ -172,6 +172,10 @@ struct dsa_switch_tree {
 | 
				
			||||||
	list_for_each_entry((_dp), &(_dst)->ports, list)	\
 | 
						list_for_each_entry((_dp), &(_dst)->ports, list)	\
 | 
				
			||||||
		if ((_dp)->lag_dev == (_lag))
 | 
							if ((_dp)->lag_dev == (_lag))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define dsa_hsr_foreach_port(_dp, _ds, _hsr)			\
 | 
				
			||||||
 | 
						list_for_each_entry((_dp), &(_ds)->dst->ports, list)	\
 | 
				
			||||||
 | 
							if ((_dp)->ds == (_ds) && (_dp)->hsr_dev == (_hsr))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline struct net_device *dsa_lag_dev(struct dsa_switch_tree *dst,
 | 
					static inline struct net_device *dsa_lag_dev(struct dsa_switch_tree *dst,
 | 
				
			||||||
					     unsigned int id)
 | 
										     unsigned int id)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -264,6 +268,7 @@ struct dsa_port {
 | 
				
			||||||
	struct phylink_config	pl_config;
 | 
						struct phylink_config	pl_config;
 | 
				
			||||||
	struct net_device	*lag_dev;
 | 
						struct net_device	*lag_dev;
 | 
				
			||||||
	bool			lag_tx_enabled;
 | 
						bool			lag_tx_enabled;
 | 
				
			||||||
 | 
						struct net_device	*hsr_dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct list_head list;
 | 
						struct list_head list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -769,6 +774,14 @@ struct dsa_switch_ops {
 | 
				
			||||||
				 struct netdev_lag_upper_info *info);
 | 
									 struct netdev_lag_upper_info *info);
 | 
				
			||||||
	int	(*port_lag_leave)(struct dsa_switch *ds, int port,
 | 
						int	(*port_lag_leave)(struct dsa_switch *ds, int port,
 | 
				
			||||||
				  struct net_device *lag);
 | 
									  struct net_device *lag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * HSR integration
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						int	(*port_hsr_join)(struct dsa_switch *ds, int port,
 | 
				
			||||||
 | 
									 struct net_device *hsr);
 | 
				
			||||||
 | 
						int	(*port_hsr_leave)(struct dsa_switch *ds, int port,
 | 
				
			||||||
 | 
									  struct net_device *hsr);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes)		\
 | 
					#define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes)		\
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,8 @@ enum {
 | 
				
			||||||
	DSA_NOTIFIER_BRIDGE_LEAVE,
 | 
						DSA_NOTIFIER_BRIDGE_LEAVE,
 | 
				
			||||||
	DSA_NOTIFIER_FDB_ADD,
 | 
						DSA_NOTIFIER_FDB_ADD,
 | 
				
			||||||
	DSA_NOTIFIER_FDB_DEL,
 | 
						DSA_NOTIFIER_FDB_DEL,
 | 
				
			||||||
 | 
						DSA_NOTIFIER_HSR_JOIN,
 | 
				
			||||||
 | 
						DSA_NOTIFIER_HSR_LEAVE,
 | 
				
			||||||
	DSA_NOTIFIER_LAG_CHANGE,
 | 
						DSA_NOTIFIER_LAG_CHANGE,
 | 
				
			||||||
	DSA_NOTIFIER_LAG_JOIN,
 | 
						DSA_NOTIFIER_LAG_JOIN,
 | 
				
			||||||
	DSA_NOTIFIER_LAG_LEAVE,
 | 
						DSA_NOTIFIER_LAG_LEAVE,
 | 
				
			||||||
| 
						 | 
					@ -100,6 +102,13 @@ struct dsa_switchdev_event_work {
 | 
				
			||||||
	u16 vid;
 | 
						u16 vid;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* DSA_NOTIFIER_HSR_* */
 | 
				
			||||||
 | 
					struct dsa_notifier_hsr_info {
 | 
				
			||||||
 | 
						struct net_device *hsr;
 | 
				
			||||||
 | 
						int sw_index;
 | 
				
			||||||
 | 
						int port;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct dsa_slave_priv {
 | 
					struct dsa_slave_priv {
 | 
				
			||||||
	/* Copy of CPU port xmit for faster access in slave transmit hot path */
 | 
						/* Copy of CPU port xmit for faster access in slave transmit hot path */
 | 
				
			||||||
	struct sk_buff *	(*xmit)(struct sk_buff *skb,
 | 
						struct sk_buff *	(*xmit)(struct sk_buff *skb,
 | 
				
			||||||
| 
						 | 
					@ -183,6 +192,8 @@ int dsa_port_vlan_del(struct dsa_port *dp,
 | 
				
			||||||
		      const struct switchdev_obj_port_vlan *vlan);
 | 
							      const struct switchdev_obj_port_vlan *vlan);
 | 
				
			||||||
int dsa_port_link_register_of(struct dsa_port *dp);
 | 
					int dsa_port_link_register_of(struct dsa_port *dp);
 | 
				
			||||||
void dsa_port_link_unregister_of(struct dsa_port *dp);
 | 
					void dsa_port_link_unregister_of(struct dsa_port *dp);
 | 
				
			||||||
 | 
					int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr);
 | 
				
			||||||
 | 
					void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr);
 | 
				
			||||||
extern const struct phylink_mac_ops dsa_port_phylink_mac_ops;
 | 
					extern const struct phylink_mac_ops dsa_port_phylink_mac_ops;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline bool dsa_port_offloads_netdev(struct dsa_port *dp,
 | 
					static inline bool dsa_port_offloads_netdev(struct dsa_port *dp,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -868,3 +868,37 @@ int dsa_port_get_phy_sset_count(struct dsa_port *dp)
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count);
 | 
					EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dsa_notifier_hsr_info info = {
 | 
				
			||||||
 | 
							.sw_index = dp->ds->index,
 | 
				
			||||||
 | 
							.port = dp->index,
 | 
				
			||||||
 | 
							.hsr = hsr,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dp->hsr_dev = hsr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_JOIN, &info);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							dp->hsr_dev = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dsa_notifier_hsr_info info = {
 | 
				
			||||||
 | 
							.sw_index = dp->ds->index,
 | 
				
			||||||
 | 
							.port = dp->index,
 | 
				
			||||||
 | 
							.hsr = hsr,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dp->hsr_dev = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_LEAVE, &info);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							pr_err("DSA: failed to notify DSA_NOTIFIER_HSR_LEAVE\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@
 | 
				
			||||||
#include <net/pkt_cls.h>
 | 
					#include <net/pkt_cls.h>
 | 
				
			||||||
#include <net/tc_act/tc_mirred.h>
 | 
					#include <net/tc_act/tc_mirred.h>
 | 
				
			||||||
#include <linux/if_bridge.h>
 | 
					#include <linux/if_bridge.h>
 | 
				
			||||||
 | 
					#include <linux/if_hsr.h>
 | 
				
			||||||
#include <linux/netpoll.h>
 | 
					#include <linux/netpoll.h>
 | 
				
			||||||
#include <linux/ptp_classify.h>
 | 
					#include <linux/ptp_classify.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1938,6 +1939,19 @@ static int dsa_slave_changeupper(struct net_device *dev,
 | 
				
			||||||
			dsa_port_lag_leave(dp, info->upper_dev);
 | 
								dsa_port_lag_leave(dp, info->upper_dev);
 | 
				
			||||||
			err = NOTIFY_OK;
 | 
								err = NOTIFY_OK;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} else if (is_hsr_master(info->upper_dev)) {
 | 
				
			||||||
 | 
							if (info->linking) {
 | 
				
			||||||
 | 
								err = dsa_port_hsr_join(dp, info->upper_dev);
 | 
				
			||||||
 | 
								if (err == -EOPNOTSUPP) {
 | 
				
			||||||
 | 
									NL_SET_ERR_MSG_MOD(info->info.extack,
 | 
				
			||||||
 | 
											   "Offloading not supported");
 | 
				
			||||||
 | 
									err = 0;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								err = notifier_from_errno(err);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								dsa_port_hsr_leave(dp, info->upper_dev);
 | 
				
			||||||
 | 
								err = NOTIFY_OK;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -166,6 +166,24 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds,
 | 
				
			||||||
	return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
 | 
						return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dsa_switch_hsr_join(struct dsa_switch *ds,
 | 
				
			||||||
 | 
								       struct dsa_notifier_hsr_info *info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (ds->index == info->sw_index && ds->ops->port_hsr_join)
 | 
				
			||||||
 | 
							return ds->ops->port_hsr_join(ds, info->port, info->hsr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dsa_switch_hsr_leave(struct dsa_switch *ds,
 | 
				
			||||||
 | 
									struct dsa_notifier_hsr_info *info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (ds->index == info->sw_index && ds->ops->port_hsr_leave)
 | 
				
			||||||
 | 
							return ds->ops->port_hsr_leave(ds, info->port, info->hsr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int dsa_switch_lag_change(struct dsa_switch *ds,
 | 
					static int dsa_switch_lag_change(struct dsa_switch *ds,
 | 
				
			||||||
				 struct dsa_notifier_lag_info *info)
 | 
									 struct dsa_notifier_lag_info *info)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -371,6 +389,12 @@ static int dsa_switch_event(struct notifier_block *nb,
 | 
				
			||||||
	case DSA_NOTIFIER_FDB_DEL:
 | 
						case DSA_NOTIFIER_FDB_DEL:
 | 
				
			||||||
		err = dsa_switch_fdb_del(ds, info);
 | 
							err = dsa_switch_fdb_del(ds, info);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case DSA_NOTIFIER_HSR_JOIN:
 | 
				
			||||||
 | 
							err = dsa_switch_hsr_join(ds, info);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case DSA_NOTIFIER_HSR_LEAVE:
 | 
				
			||||||
 | 
							err = dsa_switch_hsr_leave(ds, info);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	case DSA_NOTIFIER_LAG_CHANGE:
 | 
						case DSA_NOTIFIER_LAG_CHANGE:
 | 
				
			||||||
		err = dsa_switch_lag_change(ds, info);
 | 
							err = dsa_switch_lag_change(ds, info);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue