summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/net/dsa.h13
-rw-r--r--net/dsa/dsa_priv.h11
-rw-r--r--net/dsa/port.c34
-rw-r--r--net/dsa/slave.c14
-rw-r--r--net/dsa/switch.c24
5 files changed, 96 insertions, 0 deletions
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 60acb9fca124e..d8de23ce7221f 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -172,6 +172,10 @@ struct dsa_switch_tree {
list_for_each_entry((_dp), &(_dst)->ports, list) \
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,
unsigned int id)
{
@@ -264,6 +268,7 @@ struct dsa_port {
struct phylink_config pl_config;
struct net_device *lag_dev;
bool lag_tx_enabled;
+ struct net_device *hsr_dev;
struct list_head list;
@@ -769,6 +774,14 @@ struct dsa_switch_ops {
struct netdev_lag_upper_info *info);
int (*port_lag_leave)(struct dsa_switch *ds, int port,
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) \
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 8a1bcb2b42085..7060f128386b7 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -20,6 +20,8 @@ enum {
DSA_NOTIFIER_BRIDGE_LEAVE,
DSA_NOTIFIER_FDB_ADD,
DSA_NOTIFIER_FDB_DEL,
+ DSA_NOTIFIER_HSR_JOIN,
+ DSA_NOTIFIER_HSR_LEAVE,
DSA_NOTIFIER_LAG_CHANGE,
DSA_NOTIFIER_LAG_JOIN,
DSA_NOTIFIER_LAG_LEAVE,
@@ -100,6 +102,13 @@ struct dsa_switchdev_event_work {
u16 vid;
};
+/* DSA_NOTIFIER_HSR_* */
+struct dsa_notifier_hsr_info {
+ struct net_device *hsr;
+ int sw_index;
+ int port;
+};
+
struct dsa_slave_priv {
/* Copy of CPU port xmit for faster access in slave transmit hot path */
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);
int dsa_port_link_register_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;
static inline bool dsa_port_offloads_netdev(struct dsa_port *dp,
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 5e079a61528e0..b93bda4630263 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -868,3 +868,37 @@ int dsa_port_get_phy_sset_count(struct dsa_port *dp)
return ret;
}
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");
+}
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 431bdbdd8473f..a95e78d597402 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -17,6 +17,7 @@
#include <net/pkt_cls.h>
#include <net/tc_act/tc_mirred.h>
#include <linux/if_bridge.h>
+#include <linux/if_hsr.h>
#include <linux/netpoll.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);
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;
diff --git a/net/dsa/switch.c b/net/dsa/switch.c
index 5026e4143663f..1906179e59f78 100644
--- a/net/dsa/switch.c
+++ b/net/dsa/switch.c
@@ -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);
}
+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,
struct dsa_notifier_lag_info *info)
{
@@ -371,6 +389,12 @@ static int dsa_switch_event(struct notifier_block *nb,
case DSA_NOTIFIER_FDB_DEL:
err = dsa_switch_fdb_del(ds, info);
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:
err = dsa_switch_lag_change(ds, info);
break;