Skip to content

Commit

Permalink
Merge branch 'dsa-hsr-offload'
Browse files Browse the repository at this point in the history
George McCollister says:

====================
add HSR offloading support for DSA switches

Add support for offloading HSR/PRP (IEC 62439-3) tag insertion, tag
removal, forwarding and duplication on DSA switches.
This series adds offloading to the xrs700x DSA driver.

Changes since RFC:
 * Split hsr and dsa patches. (Florian Fainelli)

Changes since v1:
 * Fixed some typos/wording. (Vladimir Oltean)
 * eliminate IFF_HSR and use is_hsr_master instead. (Vladimir Oltean)
 * Make hsr_handle_sup_frame handle skb_std as well (required when offloading)
 * Don't add hsr tag for HSR v0 supervisory frames.
 * Fixed tag insertion offloading for PRP.

Changes since v2:
 * Return -EOPNOTSUPP instead of 0 in dsa_switch_hsr_join and
   dsa_switch_hsr_leave. (Vladimir Oltean)
 * Only allow ports 1 and 2 to be HSR/PRP redundant ports. (Tobias Waldekranz)
 * Set and remove HSR features for both redundant ports. (Vladimir Oltean)
 * Change port_hsr_leave() to return int instead of void.
 * Remove hsr_init_skb() proto argument. (Vladimir Oltean)
===================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Feb 11, 2021
2 parents f8a7e01 + bd62e6f commit e345e58
Show file tree
Hide file tree
Showing 20 changed files with 351 additions and 60 deletions.
21 changes: 21 additions & 0 deletions Documentation/networking/netdev-features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,24 @@ stricter than Hardware LRO. A packet stream merged by Hardware GRO must
be re-segmentable by GSO or TSO back to the exact original packet stream.
Hardware GRO is dependent on RXCSUM since every packet successfully merged
by hardware must also have the checksum verified by hardware.

* hsr-tag-ins-offload

This should be set for devices which insert an HSR (High-availability Seamless
Redundancy) or PRP (Parallel Redundancy Protocol) tag automatically.

* hsr-tag-rm-offload

This should be set for devices which remove HSR (High-availability Seamless
Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically.

* hsr-fwd-offload

This should be set for devices which forward HSR (High-availability Seamless
Redundancy) frames from one port to another in hardware.

* hsr-dup-offload

This should be set for devices which duplicate outgoing HSR (High-availability
Seamless Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically
frames in hardware.
121 changes: 121 additions & 0 deletions drivers/net/dsa/xrs700x/xrs700x.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@
#include <net/dsa.h>
#include <linux/if_bridge.h>
#include <linux/of_device.h>
#include <linux/netdev_features.h>
#include <linux/if_hsr.h>
#include "xrs700x.h"
#include "xrs700x_reg.h"

#define XRS700X_MIB_INTERVAL msecs_to_jiffies(3000)

#define XRS7000X_SUPPORTED_HSR_FEATURES \
(NETIF_F_HW_HSR_TAG_INS | NETIF_F_HW_HSR_TAG_RM | \
NETIF_F_HW_HSR_FWD | NETIF_F_HW_HSR_DUP)

#define XRS7003E_ID 0x100
#define XRS7003F_ID 0x101
#define XRS7004E_ID 0x200
Expand Down Expand Up @@ -496,6 +502,119 @@ static void xrs700x_bridge_leave(struct dsa_switch *ds, int port,
xrs700x_bridge_common(ds, port, bridge, false);
}

static int xrs700x_hsr_join(struct dsa_switch *ds, int port,
struct net_device *hsr)
{
unsigned int val = XRS_HSR_CFG_HSR_PRP;
struct dsa_port *partner = NULL, *dp;
struct xrs700x *priv = ds->priv;
struct net_device *slave;
int ret, i, hsr_pair[2];
enum hsr_version ver;

ret = hsr_get_version(hsr, &ver);
if (ret)
return ret;

/* Only ports 1 and 2 can be HSR/PRP redundant ports. */
if (port != 1 && port != 2)
return -EOPNOTSUPP;

if (ver == HSR_V1)
val |= XRS_HSR_CFG_HSR;
else if (ver == PRP_V1)
val |= XRS_HSR_CFG_PRP;
else
return -EOPNOTSUPP;

dsa_hsr_foreach_port(dp, ds, hsr) {
partner = dp;
}

/* We can't enable redundancy on the switch until both
* redundant ports have signed up.
*/
if (!partner)
return 0;

regmap_fields_write(priv->ps_forward, partner->index,
XRS_PORT_DISABLED);
regmap_fields_write(priv->ps_forward, port, XRS_PORT_DISABLED);

regmap_write(priv->regmap, XRS_HSR_CFG(partner->index),
val | XRS_HSR_CFG_LANID_A);
regmap_write(priv->regmap, XRS_HSR_CFG(port),
val | XRS_HSR_CFG_LANID_B);

/* Clear bits for both redundant ports (HSR only) and the CPU port to
* enable forwarding.
*/
val = GENMASK(ds->num_ports - 1, 0);
if (ver == HSR_V1) {
val &= ~BIT(partner->index);
val &= ~BIT(port);
}
val &= ~BIT(dsa_upstream_port(ds, port));
regmap_write(priv->regmap, XRS_PORT_FWD_MASK(partner->index), val);
regmap_write(priv->regmap, XRS_PORT_FWD_MASK(port), val);

regmap_fields_write(priv->ps_forward, partner->index,
XRS_PORT_FORWARDING);
regmap_fields_write(priv->ps_forward, port, XRS_PORT_FORWARDING);

hsr_pair[0] = port;
hsr_pair[1] = partner->index;
for (i = 0; i < ARRAY_SIZE(hsr_pair); i++) {
slave = dsa_to_port(ds, hsr_pair[i])->slave;
slave->features |= XRS7000X_SUPPORTED_HSR_FEATURES;
}

return 0;
}

static int xrs700x_hsr_leave(struct dsa_switch *ds, int port,
struct net_device *hsr)
{
struct dsa_port *partner = NULL, *dp;
struct xrs700x *priv = ds->priv;
struct net_device *slave;
int i, hsr_pair[2];
unsigned int val;

dsa_hsr_foreach_port(dp, ds, hsr) {
partner = dp;
}

if (!partner)
return 0;

regmap_fields_write(priv->ps_forward, partner->index,
XRS_PORT_DISABLED);
regmap_fields_write(priv->ps_forward, port, XRS_PORT_DISABLED);

regmap_write(priv->regmap, XRS_HSR_CFG(partner->index), 0);
regmap_write(priv->regmap, XRS_HSR_CFG(port), 0);

/* Clear bit for the CPU port to enable forwarding. */
val = GENMASK(ds->num_ports - 1, 0);
val &= ~BIT(dsa_upstream_port(ds, port));
regmap_write(priv->regmap, XRS_PORT_FWD_MASK(partner->index), val);
regmap_write(priv->regmap, XRS_PORT_FWD_MASK(port), val);

regmap_fields_write(priv->ps_forward, partner->index,
XRS_PORT_FORWARDING);
regmap_fields_write(priv->ps_forward, port, XRS_PORT_FORWARDING);

hsr_pair[0] = port;
hsr_pair[1] = partner->index;
for (i = 0; i < ARRAY_SIZE(hsr_pair); i++) {
slave = dsa_to_port(ds, hsr_pair[i])->slave;
slave->features &= ~XRS7000X_SUPPORTED_HSR_FEATURES;
}

return 0;
}

static const struct dsa_switch_ops xrs700x_ops = {
.get_tag_protocol = xrs700x_get_tag_protocol,
.setup = xrs700x_setup,
Expand All @@ -509,6 +628,8 @@ static const struct dsa_switch_ops xrs700x_ops = {
.get_stats64 = xrs700x_get_stats64,
.port_bridge_join = xrs700x_bridge_join,
.port_bridge_leave = xrs700x_bridge_leave,
.port_hsr_join = xrs700x_hsr_join,
.port_hsr_leave = xrs700x_hsr_leave,
};

static int xrs700x_detect(struct xrs700x *priv)
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/dsa/xrs700x/xrs700x_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@

/* Port Configuration Registers - HSR/PRP */
#define XRS_HSR_CFG(x) (XRS_PORT_HSR_BASE(x) + 0x0)
#define XRS_HSR_CFG_HSR_PRP BIT(0)
#define XRS_HSR_CFG_HSR 0
#define XRS_HSR_CFG_PRP BIT(8)
#define XRS_HSR_CFG_LANID_A 0
#define XRS_HSR_CFG_LANID_B BIT(10)

/* Port Configuration Registers - PTP */
#define XRS_PTP_RX_SYNC_DELAY_NS_LO(x) (XRS_PORT_PTP_BASE(x) + 0x2)
Expand Down
27 changes: 27 additions & 0 deletions include/linux/if_hsr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_IF_HSR_H_
#define _LINUX_IF_HSR_H_

/* used to differentiate various protocols */
enum hsr_version {
HSR_V0 = 0,
HSR_V1,
PRP_V1,
};

#if IS_ENABLED(CONFIG_HSR)
extern bool is_hsr_master(struct net_device *dev);
extern int hsr_get_version(struct net_device *dev, enum hsr_version *ver);
#else
static inline bool is_hsr_master(struct net_device *dev)
{
return false;
}
static inline int hsr_get_version(struct net_device *dev,
enum hsr_version *ver)
{
return -EINVAL;
}
#endif /* CONFIG_HSR */

#endif /*_LINUX_IF_HSR_H_*/
9 changes: 9 additions & 0 deletions include/linux/netdev_features.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ enum {
NETIF_F_HW_MACSEC_BIT, /* Offload MACsec operations */
NETIF_F_GRO_UDP_FWD_BIT, /* Allow UDP GRO for forwarding */

NETIF_F_HW_HSR_TAG_INS_BIT, /* Offload HSR tag insertion */
NETIF_F_HW_HSR_TAG_RM_BIT, /* Offload HSR tag removal */
NETIF_F_HW_HSR_FWD_BIT, /* Offload HSR forwarding */
NETIF_F_HW_HSR_DUP_BIT, /* Offload HSR duplication */

/*
* Add your fresh new feature above and remember to update
* netdev_features_strings[] in net/core/ethtool.c and maybe
Expand Down Expand Up @@ -159,6 +164,10 @@ enum {
#define NETIF_F_GSO_FRAGLIST __NETIF_F(GSO_FRAGLIST)
#define NETIF_F_HW_MACSEC __NETIF_F(HW_MACSEC)
#define NETIF_F_GRO_UDP_FWD __NETIF_F(GRO_UDP_FWD)
#define NETIF_F_HW_HSR_TAG_INS __NETIF_F(HW_HSR_TAG_INS)
#define NETIF_F_HW_HSR_TAG_RM __NETIF_F(HW_HSR_TAG_RM)
#define NETIF_F_HW_HSR_FWD __NETIF_F(HW_HSR_FWD)
#define NETIF_F_HW_HSR_DUP __NETIF_F(HW_HSR_DUP)

/* Finds the next feature with the highest number of the range of start till 0.
*/
Expand Down
13 changes: 13 additions & 0 deletions include/net/dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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) \
Expand Down
11 changes: 11 additions & 0 deletions net/dsa/dsa_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
34 changes: 34 additions & 0 deletions net/dsa/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
14 changes: 14 additions & 0 deletions net/dsa/slave.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit e345e58

Please sign in to comment.