Skip to content

Commit

Permalink
Merge branch 'bridge-mrp-Extend-br_mrp_switchdev_'
Browse files Browse the repository at this point in the history
Horatiu Vulturv says:

====================
bridge: mrp: Extend br_mrp_switchdev_*

This patch series extends MRP switchdev to allow the SW to have a better
understanding if the HW can implement the MRP functionality or it needs
to help the HW to run it. There are 3 cases:
- when HW can't implement at all the functionality.
- when HW can implement a part of the functionality but needs the SW
  implement the rest. For example if it can't detect when it stops
  receiving MRP Test frames but it can copy the MRP frames to CPU to
  allow the SW to determine this.  Another example is generating the MRP
  Test frames. If HW can't do that then the SW is used as backup.
- when HW can implement completely the functionality.

So, initially the SW tries to offload the entire functionality in HW, if
that fails it tries offload parts of the functionality in HW and use the
SW as helper and if also this fails then MRP can't run on this HW.

Based on these new calls, implement the switchdev for Ocelot driver. This
is an example where the HW can't run completely the functionality but it
can help the SW to run it, by trapping all MRP frames to CPU.

Also this patch series adds MRP support to DSA and implements the Felix
driver which just reuse the Ocelot functions. This part was just compiled
tested because I don't have any HW on which to do the actual tests.

v4:
 - remove ifdef MRP from include/net/switchdev.h
 - move MRP implementation for Ocelot in a different file such that
   Felix driver can use it.
 - extend DSA with MRP support
 - implement MRP support for Felix.
v3:
 - implement the switchdev calls needed by Ocelot driver.
v2:
 - fix typos in comments and in commit messages
 - remove some of the comments
 - move repeated code in helper function
 - fix issue when deleting a node when sw_backup was true
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Feb 16, 2021
2 parents 06b334f + a026c50 commit 43d42e6
Show file tree
Hide file tree
Showing 17 changed files with 715 additions and 104 deletions.
38 changes: 38 additions & 0 deletions drivers/net/dsa/ocelot/felix.c
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,40 @@ static int felix_sb_occ_tc_port_bind_get(struct dsa_switch *ds, int port,
pool_type, p_cur, p_max);
}

static int felix_mrp_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_mrp *mrp)
{
struct ocelot *ocelot = ds->priv;

return ocelot_mrp_add(ocelot, port, mrp);
}

static int felix_mrp_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_mrp *mrp)
{
struct ocelot *ocelot = ds->priv;

return ocelot_mrp_add(ocelot, port, mrp);
}

static int
felix_mrp_add_ring_role(struct dsa_switch *ds, int port,
const struct switchdev_obj_ring_role_mrp *mrp)
{
struct ocelot *ocelot = ds->priv;

return ocelot_mrp_add_ring_role(ocelot, port, mrp);
}

static int
felix_mrp_del_ring_role(struct dsa_switch *ds, int port,
const struct switchdev_obj_ring_role_mrp *mrp)
{
struct ocelot *ocelot = ds->priv;

return ocelot_mrp_del_ring_role(ocelot, port, mrp);
}

const struct dsa_switch_ops felix_switch_ops = {
.get_tag_protocol = felix_get_tag_protocol,
.change_tag_protocol = felix_change_tag_protocol,
Expand Down Expand Up @@ -1636,6 +1670,10 @@ const struct dsa_switch_ops felix_switch_ops = {
.devlink_sb_occ_max_clear = felix_sb_occ_max_clear,
.devlink_sb_occ_port_pool_get = felix_sb_occ_port_pool_get,
.devlink_sb_occ_tc_port_bind_get= felix_sb_occ_tc_port_bind_get,
.port_mrp_add = felix_mrp_add,
.port_mrp_del = felix_mrp_del,
.port_mrp_add_ring_role = felix_mrp_add_ring_role,
.port_mrp_del_ring_role = felix_mrp_del_ring_role,
};

struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port)
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/mscc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mscc_ocelot_switch_lib-y := \
ocelot_flower.o \
ocelot_ptp.o \
ocelot_devlink.o
mscc_ocelot_switch_lib-$(CONFIG_BRIDGE_MRP) += ocelot_mrp.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o
mscc_ocelot-y := \
ocelot_vsc7514.o \
Expand Down
10 changes: 9 additions & 1 deletion drivers/net/ethernet/mscc/ocelot.c
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ static int ocelot_xtr_poll_xfh(struct ocelot *ocelot, int grp, u32 *xfh)
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
{
struct skb_shared_hwtstamps *shhwtstamps;
u64 tod_in_ns, full_ts_in_ns;
u64 tod_in_ns, full_ts_in_ns, cpuq;
u64 timestamp, src_port, len;
u32 xfh[OCELOT_TAG_LEN / 4];
struct net_device *dev;
Expand All @@ -704,6 +704,7 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
ocelot_xfh_get_src_port(xfh, &src_port);
ocelot_xfh_get_len(xfh, &len);
ocelot_xfh_get_rew_val(xfh, &timestamp);
ocelot_xfh_get_cpuq(xfh, &cpuq);

if (WARN_ON(src_port >= ocelot->num_phys_ports))
return -EINVAL;
Expand Down Expand Up @@ -770,6 +771,13 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
skb->offload_fwd_mark = 1;

skb->protocol = eth_type_trans(skb, dev);

#if IS_ENABLED(CONFIG_BRIDGE_MRP)
if (skb->protocol == cpu_to_be16(ETH_P_MRP) &&
cpuq & BIT(OCELOT_MRP_CPUQ))
skb->offload_fwd_mark = 0;
#endif

*nskb = skb;

return 0;
Expand Down
175 changes: 175 additions & 0 deletions drivers/net/ethernet/mscc/ocelot_mrp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/* Microsemi Ocelot Switch driver
*
* This contains glue logic between the switchdev driver operations and the
* mscc_ocelot_switch_lib.
*
* Copyright (c) 2017, 2019 Microsemi Corporation
* Copyright 2020-2021 NXP Semiconductors
*/

#include <linux/if_bridge.h>
#include <linux/mrp_bridge.h>
#include <soc/mscc/ocelot_vcap.h>
#include <uapi/linux/mrp_bridge.h>
#include "ocelot.h"
#include "ocelot_vcap.h"

static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int port)
{
struct ocelot_vcap_block *block_vcap_is2;
struct ocelot_vcap_filter *filter;

block_vcap_is2 = &ocelot->block[VCAP_IS2];
filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, port,
false);
if (!filter)
return 0;

return ocelot_vcap_filter_del(ocelot, filter);
}

int ocelot_mrp_add(struct ocelot *ocelot, int port,
const struct switchdev_obj_mrp *mrp)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot_port_private *priv;
struct net_device *dev;

if (!ocelot_port)
return -EOPNOTSUPP;

priv = container_of(ocelot_port, struct ocelot_port_private, port);
dev = priv->dev;

if (mrp->p_port != dev && mrp->s_port != dev)
return 0;

if (ocelot->mrp_ring_id != 0 &&
ocelot->mrp_s_port &&
ocelot->mrp_p_port)
return -EINVAL;

if (mrp->p_port == dev)
ocelot->mrp_p_port = dev;

if (mrp->s_port == dev)
ocelot->mrp_s_port = dev;

ocelot->mrp_ring_id = mrp->ring_id;

return 0;
}
EXPORT_SYMBOL(ocelot_mrp_add);

int ocelot_mrp_del(struct ocelot *ocelot, int port,
const struct switchdev_obj_mrp *mrp)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot_port_private *priv;
struct net_device *dev;

if (!ocelot_port)
return -EOPNOTSUPP;

priv = container_of(ocelot_port, struct ocelot_port_private, port);
dev = priv->dev;

if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
return 0;

if (ocelot->mrp_ring_id == 0 &&
!ocelot->mrp_s_port &&
!ocelot->mrp_p_port)
return -EINVAL;

if (ocelot_mrp_del_vcap(ocelot, priv->chip_port))
return -EINVAL;

if (ocelot->mrp_p_port == dev)
ocelot->mrp_p_port = NULL;

if (ocelot->mrp_s_port == dev)
ocelot->mrp_s_port = NULL;

ocelot->mrp_ring_id = 0;

return 0;
}
EXPORT_SYMBOL(ocelot_mrp_del);

int ocelot_mrp_add_ring_role(struct ocelot *ocelot, int port,
const struct switchdev_obj_ring_role_mrp *mrp)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot_vcap_filter *filter;
struct ocelot_port_private *priv;
struct net_device *dev;
int err;

if (!ocelot_port)
return -EOPNOTSUPP;

priv = container_of(ocelot_port, struct ocelot_port_private, port);
dev = priv->dev;

if (ocelot->mrp_ring_id != mrp->ring_id)
return -EINVAL;

if (!mrp->sw_backup)
return -EOPNOTSUPP;

if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
return 0;

filter = kzalloc(sizeof(*filter), GFP_ATOMIC);
if (!filter)
return -ENOMEM;

filter->key_type = OCELOT_VCAP_KEY_ETYPE;
filter->prio = 1;
filter->id.cookie = priv->chip_port;
filter->id.tc_offload = false;
filter->block_id = VCAP_IS2;
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
filter->ingress_port_mask = BIT(priv->chip_port);
*(__be16 *)filter->key.etype.etype.value = htons(ETH_P_MRP);
*(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
filter->action.port_mask = 0x0;
filter->action.cpu_copy_ena = true;
filter->action.cpu_qu_num = OCELOT_MRP_CPUQ;

err = ocelot_vcap_filter_add(ocelot, filter, NULL);
if (err)
kfree(filter);

return err;
}
EXPORT_SYMBOL(ocelot_mrp_add_ring_role);

int ocelot_mrp_del_ring_role(struct ocelot *ocelot, int port,
const struct switchdev_obj_ring_role_mrp *mrp)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot_port_private *priv;
struct net_device *dev;

if (!ocelot_port)
return -EOPNOTSUPP;

priv = container_of(ocelot_port, struct ocelot_port_private, port);
dev = priv->dev;

if (ocelot->mrp_ring_id != mrp->ring_id)
return -EINVAL;

if (!mrp->sw_backup)
return -EOPNOTSUPP;

if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
return 0;

return ocelot_mrp_del_vcap(ocelot, priv->chip_port);
}
EXPORT_SYMBOL(ocelot_mrp_del_ring_role);
60 changes: 60 additions & 0 deletions drivers/net/ethernet/mscc/ocelot_net.c
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,52 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
return ocelot_port_mdb_del(ocelot, port, mdb);
}

static int ocelot_port_obj_mrp_add(struct net_device *dev,
const struct switchdev_obj_mrp *mrp)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;

return ocelot_mrp_add(ocelot, port, mrp);
}

static int ocelot_port_obj_mrp_del(struct net_device *dev,
const struct switchdev_obj_mrp *mrp)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;

return ocelot_mrp_del(ocelot, port, mrp);
}

static int
ocelot_port_obj_mrp_add_ring_role(struct net_device *dev,
const struct switchdev_obj_ring_role_mrp *mrp)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;

return ocelot_mrp_add_ring_role(ocelot, port, mrp);
}

static int
ocelot_port_obj_mrp_del_ring_role(struct net_device *dev,
const struct switchdev_obj_ring_role_mrp *mrp)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;

return ocelot_mrp_del_ring_role(ocelot, port, mrp);
}

static int ocelot_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj,
struct netlink_ext_ack *extack)
Expand All @@ -1024,6 +1070,13 @@ static int ocelot_port_obj_add(struct net_device *dev,
case SWITCHDEV_OBJ_ID_PORT_MDB:
ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
break;
case SWITCHDEV_OBJ_ID_MRP:
ret = ocelot_port_obj_mrp_add(dev, SWITCHDEV_OBJ_MRP(obj));
break;
case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
ret = ocelot_port_obj_mrp_add_ring_role(dev,
SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
break;
default:
return -EOPNOTSUPP;
}
Expand All @@ -1044,6 +1097,13 @@ static int ocelot_port_obj_del(struct net_device *dev,
case SWITCHDEV_OBJ_ID_PORT_MDB:
ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
break;
case SWITCHDEV_OBJ_ID_MRP:
ret = ocelot_port_obj_mrp_del(dev, SWITCHDEV_OBJ_MRP(obj));
break;
case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
ret = ocelot_port_obj_mrp_del_ring_role(dev,
SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
break;
default:
return -EOPNOTSUPP;
}
Expand Down
5 changes: 5 additions & 0 deletions include/linux/dsa/ocelot.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ static inline void ocelot_xfh_get_src_port(void *extraction, u64 *src_port)
packing(extraction, src_port, 46, 43, OCELOT_TAG_LEN, UNPACK, 0);
}

static inline void ocelot_xfh_get_cpuq(void *extraction, u64 *cpuq)
{
packing(extraction, cpuq, 28, 20, OCELOT_TAG_LEN, UNPACK, 0);
}

static inline void ocelot_xfh_get_qos_class(void *extraction, u64 *qos_class)
{
packing(extraction, qos_class, 19, 17, OCELOT_TAG_LEN, UNPACK, 0);
Expand Down
12 changes: 12 additions & 0 deletions include/net/dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,18 @@ struct dsa_switch_ops {
struct net_device *hsr);
int (*port_hsr_leave)(struct dsa_switch *ds, int port,
struct net_device *hsr);

/*
* MRP integration
*/
int (*port_mrp_add)(struct dsa_switch *ds, int port,
const struct switchdev_obj_mrp *mrp);
int (*port_mrp_del)(struct dsa_switch *ds, int port,
const struct switchdev_obj_mrp *mrp);
int (*port_mrp_add_ring_role)(struct dsa_switch *ds, int port,
const struct switchdev_obj_ring_role_mrp *mrp);
int (*port_mrp_del_ring_role)(struct dsa_switch *ds, int port,
const struct switchdev_obj_ring_role_mrp *mrp);
};

#define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \
Expand Down
Loading

0 comments on commit 43d42e6

Please sign in to comment.