I noticed that my Ten64 doesn’t forward packets to port 3386 over a linux bridge, so I went down a bit of a rabbit hole.
The Ten64’s DPAA2 hardware discards some valid frames before Linux sees them: TCP to port 3386, UDP to 2123/2152/3386 unless the payload is GTP, IPv6 UDP to 4500/4789 with fewer than 8 bytes of payload, and frames with IPv4 header errors. The only trace is [hw] rx discarded frames in ethtool -S. Cause: dpaa2-eth asks the MC to discard every frame with any parser or checksum error bit, and the WRIOP parser guesses layer-5 protocols by port number.
The fix I came up with: discard only frame-length and physical errors and let the stack recheck checksums.
Patch 1/2: dpaa2-eth checksum guard
diff --git c/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c i/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index 6f1046c9cc51..1745867723a1 100644
--- c/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ i/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -129,6 +129,10 @@ static void dpaa2_eth_validate_rx_csum(struct dpaa2_eth_priv *priv,
(fd_status & DPAA2_FAS_L4CV)))
return;
+ /* The hardware validated the checksums and found an error */
+ if (fd_status & (DPAA2_FAS_L3CE | DPAA2_FAS_L4CE))
+ return;
+
/* Inform the stack there's no need to compute L3/L4 csum anymore */
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
Patch 2/2: dpaa2-eth pass frames with parser errors
diff --git i/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c w/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c
index 8775c931106b..428354013c15 100644
--- i/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c
+++ w/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c
@@ -143,28 +143,17 @@ static int dpaa2_eth_dl_trap_group_action_set(struct devlink *devlink,
struct dpaa2_eth_priv *priv = dl_priv->dpaa2_priv;
struct net_device *net_dev = priv->net_dev;
struct device *dev = net_dev->dev.parent;
- struct dpni_error_cfg err_cfg = {0};
int err;
if (group->id != DEVLINK_TRAP_GROUP_GENERIC_ID_PARSER_ERROR_DROPS)
return -EOPNOTSUPP;
- /* Configure handling of frames marked as errors from the parser */
- err_cfg.errors = DPAA2_FAS_RX_ERR_MASK;
- err_cfg.set_frame_annotation = 1;
-
- switch (action) {
- case DEVLINK_TRAP_ACTION_DROP:
- err_cfg.error_action = DPNI_ERROR_ACTION_DISCARD;
- break;
- case DEVLINK_TRAP_ACTION_TRAP:
- err_cfg.error_action = DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE;
- break;
- default:
+ if (action != DEVLINK_TRAP_ACTION_DROP &&
+ action != DEVLINK_TRAP_ACTION_TRAP)
return -EOPNOTSUPP;
- }
- err = dpni_set_errors_behavior(priv->mc_io, 0, priv->mc_token, &err_cfg);
+ err = dpaa2_eth_set_rx_err_behavior(priv,
+ action == DEVLINK_TRAP_ACTION_TRAP);
if (err) {
dev_err(dev, "dpni_set_errors_behavior failed\n");
return err;
diff --git i/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c w/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index 1745867723a1..1849a546c8e3 100644
--- i/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ w/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -4373,6 +4373,37 @@ static int dpaa2_eth_set_default_cls(struct dpaa2_eth_priv *priv)
return 0;
}
+/*
+ * Drop broken frames and pass frames with other ingress errors to the stack,
+ * or send all frames with errors to the Rx error queue for devlink to trap.
+ */
+int dpaa2_eth_set_rx_err_behavior(struct dpaa2_eth_priv *priv, bool trap)
+{
+ struct dpni_error_cfg err_cfg = {0};
+ int err;
+
+ err_cfg.set_frame_annotation = 1;
+
+ if (trap) {
+ err_cfg.errors = DPAA2_FAS_RX_ERR_MASK;
+ err_cfg.error_action = DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE;
+ return dpni_set_errors_behavior(priv->mc_io, 0, priv->mc_token,
+ &err_cfg);
+ }
+
+ err_cfg.errors = DPAA2_FAS_RX_DROP_ERR_MASK;
+ err_cfg.error_action = DPNI_ERROR_ACTION_DISCARD;
+ err = dpni_set_errors_behavior(priv->mc_io, 0, priv->mc_token,
+ &err_cfg);
+ if (err)
+ return err;
+
+ err_cfg.errors = DPAA2_FAS_RX_ERR_MASK & ~DPAA2_FAS_RX_DROP_ERR_MASK;
+ err_cfg.error_action = DPNI_ERROR_ACTION_CONTINUE;
+ return dpni_set_errors_behavior(priv->mc_io, 0, priv->mc_token,
+ &err_cfg);
+}
+
/* Bind the DPNI to its needed objects and resources: buffer pool, DPIOs,
* frame queues and channels
*/
@@ -4382,7 +4413,6 @@ static int dpaa2_eth_bind_dpni(struct dpaa2_eth_priv *priv)
struct net_device *net_dev = priv->net_dev;
struct dpni_pools_cfg pools_params = { 0 };
struct device *dev = net_dev->dev.parent;
- struct dpni_error_cfg err_cfg;
int err = 0;
int i;
@@ -4411,11 +4441,7 @@ static int dpaa2_eth_bind_dpni(struct dpaa2_eth_priv *priv)
dev_err(dev, "Failed to configure Rx classification key\n");
/* Configure handling of error frames */
- err_cfg.errors = DPAA2_FAS_RX_ERR_MASK;
- err_cfg.set_frame_annotation = 1;
- err_cfg.error_action = DPNI_ERROR_ACTION_DISCARD;
- err = dpni_set_errors_behavior(priv->mc_io, 0, priv->mc_token,
- &err_cfg);
+ err = dpaa2_eth_set_rx_err_behavior(priv, false);
if (err) {
dev_err(dev, "dpni_set_errors_behavior failed\n");
return err;
diff --git i/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h w/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
index 834cba8c3a41..374dad7f0b5e 100644
--- i/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
+++ w/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
@@ -355,6 +355,13 @@ static inline struct dpaa2_faead *dpaa2_get_faead(void *buf_addr, bool swa)
DPAA2_FAS_BLE | \
DPAA2_FAS_L3CE | \
DPAA2_FAS_L4CE)
+/*
+ * Ingress errors on which the frame is dropped: the frame itself is broken.
+ * The other errors come from the parser, the classifier and the checksum
+ * validation, and frames which are valid on the wire raise them too, e.g. a
+ * UDP datagram to the GTP-U port whose payload is not GTP.
+ */
+#define DPAA2_FAS_RX_DROP_ERR_MASK (DPAA2_FAS_FLE | DPAA2_FAS_FPE)
/* Time in milliseconds between link state updates */
#define DPAA2_ETH_LINK_STATE_REFRESH 1000
@@ -785,6 +792,7 @@ static inline bool dpaa2_eth_has_mac(struct dpaa2_eth_priv *priv)
int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags);
int dpaa2_eth_set_cls(struct net_device *net_dev, u64 key);
+int dpaa2_eth_set_rx_err_behavior(struct dpaa2_eth_priv *priv, bool trap);
int dpaa2_eth_cls_key_size(u64 key);
int dpaa2_eth_cls_fld_off(int prot, int field);
void dpaa2_eth_cls_trim_rule(void *key_mem, u64 fields);
What do you think?