DPAA2 drops frames on some TCP/UDP ports

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?