Merge branch 'mlxsw-mc-route-offload'

Jiri Pirko says:

====================
mlxsw: Add support for partial multicast route offload

Yotam says:

Previous patchset introduced support for offloading multicast MFC routes to
the Spectrum hardware. As described in that patchset, no partial offloading
is supported, i.e if a route has one output interface which is not a valid
offloadable device (e.g. pimreg device, dummy device, management NIC), the
route is trapped to the CPU and the forwarding is done in slow-path.

Add support for partial offloading of multicast routes, by letting the
hardware to forward the packet to all the in-hardware devices, while the
kernel ipmr module will continue forwarding to all other interfaces.

Similarly to the bridge, the kernel ipmr module will forward a marked
packet to an interface only if the interface has a different parent ID than
the packet's ingress interfaces.

The first patch introduces the offload_mr_fwd_mark skb field, which can be
used by offloading drivers to indicate that a packet had already gone
through multicast forwarding in hardware, similarly to the offload_fwd_mark
field that indicates that a packet had already gone through L2 forwarding
in hardware.

Patches 2 and 3 change the ipmr module to not forward packets that had
already been forwarded by the hardware, i.e. packets that are marked with
offload_mr_fwd_mark and the ingress VIF shares the same parent ID with the
egress VIF.

Patches 4, 5, 6 and 7 add the support in the mlxsw Spectrum driver for trap
and forward routes, while marking the trapped packets with the
offload_mr_fwd_mark.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-10-03 10:06:31 -07:00
commit da885b614a
10 changed files with 96 additions and 14 deletions

View File

@ -676,6 +676,7 @@ enum mlxsw_afa_trapdisc_trap_action {
MLXSW_ITEM32(afa, trapdisc, trap_action, 0x00, 24, 4);
enum mlxsw_afa_trapdisc_forward_action {
MLXSW_AFA_TRAPDISC_FORWARD_ACTION_FORWARD = 1,
MLXSW_AFA_TRAPDISC_FORWARD_ACTION_DISCARD = 3,
};
@ -729,6 +730,22 @@ int mlxsw_afa_block_append_trap(struct mlxsw_afa_block *block, u16 trap_id)
}
EXPORT_SYMBOL(mlxsw_afa_block_append_trap);
int mlxsw_afa_block_append_trap_and_forward(struct mlxsw_afa_block *block,
u16 trap_id)
{
char *act = mlxsw_afa_block_append_action(block,
MLXSW_AFA_TRAPDISC_CODE,
MLXSW_AFA_TRAPDISC_SIZE);
if (!act)
return -ENOBUFS;
mlxsw_afa_trapdisc_pack(act, MLXSW_AFA_TRAPDISC_TRAP_ACTION_TRAP,
MLXSW_AFA_TRAPDISC_FORWARD_ACTION_FORWARD,
trap_id);
return 0;
}
EXPORT_SYMBOL(mlxsw_afa_block_append_trap_and_forward);
/* Forwarding Action
* -----------------
* Forwarding Action can be used to implement Policy Based Switching (PBS)

View File

@ -61,6 +61,8 @@ int mlxsw_afa_block_continue(struct mlxsw_afa_block *block);
int mlxsw_afa_block_jump(struct mlxsw_afa_block *block, u16 group_id);
int mlxsw_afa_block_append_drop(struct mlxsw_afa_block *block);
int mlxsw_afa_block_append_trap(struct mlxsw_afa_block *block, u16 trap_id);
int mlxsw_afa_block_append_trap_and_forward(struct mlxsw_afa_block *block,
u16 trap_id);
int mlxsw_afa_block_append_fwd(struct mlxsw_afa_block *block,
u8 local_port, bool in_port);
int mlxsw_afa_block_append_vlan_modify(struct mlxsw_afa_block *block,

View File

@ -3312,6 +3312,14 @@ static void mlxsw_sp_rx_listener_mark_func(struct sk_buff *skb, u8 local_port,
return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv);
}
static void mlxsw_sp_rx_listener_mr_mark_func(struct sk_buff *skb,
u8 local_port, void *priv)
{
skb->offload_mr_fwd_mark = 1;
skb->offload_fwd_mark = 1;
return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv);
}
static void mlxsw_sp_rx_listener_sample_func(struct sk_buff *skb, u8 local_port,
void *priv)
{
@ -3355,6 +3363,10 @@ static void mlxsw_sp_rx_listener_sample_func(struct sk_buff *skb, u8 local_port,
MLXSW_RXL(mlxsw_sp_rx_listener_mark_func, _trap_id, _action, \
_is_ctrl, SP_##_trap_group, DISCARD)
#define MLXSW_SP_RXL_MR_MARK(_trap_id, _action, _trap_group, _is_ctrl) \
MLXSW_RXL(mlxsw_sp_rx_listener_mr_mark_func, _trap_id, _action, \
_is_ctrl, SP_##_trap_group, DISCARD)
#define MLXSW_SP_EVENTL(_func, _trap_id) \
MLXSW_EVENTL(_func, _trap_id, SP_EVENT)
@ -3425,6 +3437,7 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = {
MLXSW_SP_RXL_MARK(IPV4_PIM, TRAP_TO_CPU, PIM, false),
MLXSW_SP_RXL_MARK(RPF, TRAP_TO_CPU, RPF, false),
MLXSW_SP_RXL_MARK(ACL1, TRAP_TO_CPU, MULTICAST, false),
MLXSW_SP_RXL_MR_MARK(ACL2, TRAP_TO_CPU, MULTICAST, false),
};
static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core)

View File

@ -114,9 +114,9 @@ static bool mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif *vif)
return mlxsw_sp_mr_vif_regular(vif) && vif->dev && vif->rif;
}
static bool mlxsw_sp_mr_vif_rif_invalid(const struct mlxsw_sp_mr_vif *vif)
static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif)
{
return mlxsw_sp_mr_vif_regular(vif) && vif->dev && !vif->rif;
return vif->dev;
}
static bool
@ -182,14 +182,13 @@ mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
if (!mlxsw_sp_mr_route_valid_evifs_num(mr_route))
return MLXSW_SP_MR_ROUTE_ACTION_TRAP;
/* If either one of the eVIFs is not regular (VIF of type pimreg or
* tunnel) or one of the VIFs has no matching RIF, trap the packet.
/* If one of the eVIFs has no RIF, trap-and-forward the route as there
* is some more routing to do in software too.
*/
list_for_each_entry(rve, &mr_route->evif_list, route_node) {
if (!mlxsw_sp_mr_vif_regular(rve->mr_vif) ||
mlxsw_sp_mr_vif_rif_invalid(rve->mr_vif))
return MLXSW_SP_MR_ROUTE_ACTION_TRAP;
}
list_for_each_entry(rve, &mr_route->evif_list, route_node)
if (mlxsw_sp_mr_vif_exists(rve->mr_vif) && !rve->mr_vif->rif)
return MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD;
return MLXSW_SP_MR_ROUTE_ACTION_FORWARD;
}

View File

@ -42,6 +42,7 @@
enum mlxsw_sp_mr_route_action {
MLXSW_SP_MR_ROUTE_ACTION_FORWARD,
MLXSW_SP_MR_ROUTE_ACTION_TRAP,
MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD,
};
enum mlxsw_sp_mr_route_prio {

View File

@ -253,6 +253,7 @@ mlxsw_sp_mr_tcam_afa_block_create(struct mlxsw_sp *mlxsw_sp,
if (err)
goto err;
break;
case MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD:
case MLXSW_SP_MR_ROUTE_ACTION_FORWARD:
/* If we are about to append a multicast router action, commit
* the erif_list.
@ -266,6 +267,13 @@ mlxsw_sp_mr_tcam_afa_block_create(struct mlxsw_sp *mlxsw_sp,
erif_list->kvdl_index);
if (err)
goto err;
if (route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD) {
err = mlxsw_afa_block_append_trap_and_forward(afa_block,
MLXSW_TRAP_ID_ACL2);
if (err)
goto err;
}
break;
default:
err = -EINVAL;

View File

@ -93,6 +93,8 @@ enum {
MLXSW_TRAP_ID_ACL0 = 0x1C0,
/* Multicast trap used for routes with trap action */
MLXSW_TRAP_ID_ACL1 = 0x1C1,
/* Multicast trap used for routes with trap-and-forward action */
MLXSW_TRAP_ID_ACL2 = 0x1C2,
MLXSW_TRAP_ID_MAX = 0x1FF
};

View File

@ -57,6 +57,7 @@ static inline bool ipmr_rule_default(const struct fib_rule *rule)
struct vif_device {
struct net_device *dev; /* Device we are using */
struct netdev_phys_item_id dev_parent_id; /* Device parent ID */
unsigned long bytes_in,bytes_out;
unsigned long pkt_in,pkt_out; /* Statistics */
unsigned long rate_limit; /* Traffic shaping (NI) */

View File

@ -772,6 +772,7 @@ struct sk_buff {
__u8 remcsum_offload:1;
#ifdef CONFIG_NET_SWITCHDEV
__u8 offload_fwd_mark:1;
__u8 offload_mr_fwd_mark:1;
#endif
#ifdef CONFIG_NET_CLS_ACT
__u8 tc_skip_classify:1;

View File

@ -67,6 +67,7 @@
#include <net/fib_rules.h>
#include <linux/netconf.h>
#include <net/nexthop.h>
#include <net/switchdev.h>
struct ipmr_rule {
struct fib_rule common;
@ -868,6 +869,9 @@ static int vif_add(struct net *net, struct mr_table *mrt,
struct vifctl *vifc, int mrtsock)
{
int vifi = vifc->vifc_vifi;
struct switchdev_attr attr = {
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
};
struct vif_device *v = &mrt->vif_table[vifi];
struct net_device *dev;
struct in_device *in_dev;
@ -942,6 +946,13 @@ static int vif_add(struct net *net, struct mr_table *mrt,
/* Fill in the VIF structures */
attr.orig_dev = dev;
if (!switchdev_port_attr_get(dev, &attr)) {
memcpy(v->dev_parent_id.id, attr.u.ppid.id, attr.u.ppid.id_len);
v->dev_parent_id.id_len = attr.u.ppid.id_len;
} else {
v->dev_parent_id.id_len = 0;
}
v->rate_limit = vifc->vifc_rate_limit;
v->local = vifc->vifc_lcl_addr.s_addr;
v->remote = vifc->vifc_rmt_addr.s_addr;
@ -1848,10 +1859,33 @@ static inline int ipmr_forward_finish(struct net *net, struct sock *sk,
return dst_output(net, sk, skb);
}
#ifdef CONFIG_NET_SWITCHDEV
static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt,
int in_vifi, int out_vifi)
{
struct vif_device *out_vif = &mrt->vif_table[out_vifi];
struct vif_device *in_vif = &mrt->vif_table[in_vifi];
if (!skb->offload_mr_fwd_mark)
return false;
if (!out_vif->dev_parent_id.id_len || !in_vif->dev_parent_id.id_len)
return false;
return netdev_phys_item_id_same(&out_vif->dev_parent_id,
&in_vif->dev_parent_id);
}
#else
static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt,
int in_vifi, int out_vifi)
{
return false;
}
#endif
/* Processing handlers for ipmr_forward */
static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
struct sk_buff *skb, struct mfc_cache *c, int vifi)
int in_vifi, struct sk_buff *skb,
struct mfc_cache *c, int vifi)
{
const struct iphdr *iph = ip_hdr(skb);
struct vif_device *vif = &mrt->vif_table[vifi];
@ -1872,6 +1906,9 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
goto out_free;
}
if (ipmr_forward_offloaded(skb, mrt, in_vifi, vifi))
goto out_free;
if (vif->flags & VIFF_TUNNEL) {
rt = ip_route_output_ports(net, &fl4, NULL,
vif->remote, vif->local,
@ -2049,8 +2086,8 @@ static void ip_mr_forward(struct net *net, struct mr_table *mrt,
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2)
ipmr_queue_xmit(net, mrt, skb2, cache,
psend);
ipmr_queue_xmit(net, mrt, true_vifi,
skb2, cache, psend);
}
psend = ct;
}
@ -2061,9 +2098,10 @@ static void ip_mr_forward(struct net *net, struct mr_table *mrt,
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2)
ipmr_queue_xmit(net, mrt, skb2, cache, psend);
ipmr_queue_xmit(net, mrt, true_vifi, skb2,
cache, psend);
} else {
ipmr_queue_xmit(net, mrt, skb, cache, psend);
ipmr_queue_xmit(net, mrt, true_vifi, skb, cache, psend);
return;
}
}