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:
commit
da885b614a
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue