| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/01 15:46 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"HSR"
],
"FocusSymbols": [
"hsr_forward_skb",
"dev_disable_gro",
"netif_disable_gro",
"hsr_dev_xmit",
"hsr_handle_frame"
],
"Reasoning": "The patch adds `dev_disable_gro` and `netif_disable_gro` to disable GRO on net devices, and modifies the HSR driver to unfold GSO super-packets in `hsr_forward_skb`. It also disables GRO on HSR slave devices. These are functional changes to core networking and the HSR driver that can be reached by sending traffic through an HSR interface, making them highly relevant for fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/01 15:46 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 8cf755a9d92b390817960e73fe82736d0f40ad79\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sat Aug 1 15:46:28 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/linux/netdevice.h b/include/linux/netdevice.h\nindex 9981d637f8b54..eba2c26a49ba8 100644\n--- a/include/linux/netdevice.h\n+++ b/include/linux/netdevice.h\n@@ -3434,6 +3434,8 @@ void dev_close(struct net_device *dev);\n void netif_close_many(struct list_head *head, bool unlink);\n void netif_disable_lro(struct net_device *dev);\n void dev_disable_lro(struct net_device *dev);\n+void netif_disable_gro(struct net_device *dev);\n+void dev_disable_gro(struct net_device *dev);\n int dev_loopback_xmit(struct net *net, struct sock *sk, struct sk_buff *newskb);\n u16 dev_pick_tx_zero(struct net_device *dev, struct sk_buff *skb,\n \t\t struct net_device *sb_dev);\ndiff --git a/net/core/dev.c b/net/core/dev.c\nindex 5933c5dab09ee..a6cf2adc86256 100644\n--- a/net/core/dev.c\n+++ b/net/core/dev.c\n@@ -1840,6 +1840,24 @@ void netif_disable_lro(struct net_device *dev)\n \t}\n }\n \n+void netif_disable_gro(struct net_device *dev)\n+{\n+\tstruct net_device *lower_dev;\n+\tstruct list_head *iter;\n+\n+\tdev-\u003ewanted_features \u0026= ~(NETIF_F_GRO | NETIF_F_GRO_HW);\n+\tnetdev_update_features(dev);\n+\n+\tif (unlikely(dev-\u003efeatures \u0026 (NETIF_F_GRO | NETIF_F_GRO_HW)))\n+\t\tnetdev_WARN(dev, \"failed to disable GRO!\\n\");\n+\n+\tnetdev_for_each_lower_dev(dev, lower_dev, iter) {\n+\t\tnetdev_lock_ops(lower_dev);\n+\t\tnetif_disable_gro(lower_dev);\n+\t\tnetdev_unlock_ops(lower_dev);\n+\t}\n+}\n+\n /**\n *\tdev_disable_gro_hw - disable HW Generic Receive Offload on a device\n *\t@dev: device\ndiff --git a/net/core/dev_api.c b/net/core/dev_api.c\nindex 437947dd08ed5..02fb216295128 100644\n--- a/net/core/dev_api.c\n+++ b/net/core/dev_api.c\n@@ -269,6 +269,22 @@ void dev_disable_lro(struct net_device *dev)\n }\n EXPORT_SYMBOL(dev_disable_lro);\n \n+/**\n+ * dev_disable_gro() - disable Generic Receive Offload on a device\n+ * @dev: device\n+ *\n+ * Disable Generic Receive Offload (GRO) on a net device. Must be\n+ * called under RTNL. This is needed if received packets may be\n+ * forwarded to another interface.\n+ */\n+void dev_disable_gro(struct net_device *dev)\n+{\n+\tnetdev_lock_ops(dev);\n+\tnetif_disable_gro(dev);\n+\tnetdev_unlock_ops(dev);\n+}\n+EXPORT_SYMBOL(dev_disable_gro);\n+\n /**\n * dev_set_promiscuity() - update promiscuity count on a device\n * @dev: device\ndiff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c\nindex 5555b71ab19b5..248cbb142e21e 100644\n--- a/net/hsr/hsr_device.c\n+++ b/net/hsr/hsr_device.c\n@@ -232,9 +232,7 @@ static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)\n \t\tskb-\u003edev = master-\u003edev;\n \t\tskb_reset_mac_header(skb);\n \t\tskb_reset_mac_len(skb);\n-\t\tspin_lock_bh(\u0026hsr-\u003eseqnr_lock);\n \t\thsr_forward_skb(skb, master);\n-\t\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\n \t} else {\n \t\tdev_core_stats_tx_dropped_inc(dev);\n \t\tdev_kfree_skb_any(skb);\n@@ -335,6 +333,7 @@ static void send_hsr_supervision_frame(struct hsr_port *port,\n \t\thsr_stag-\u003esequence_nr = htons(hsr-\u003esequence_nr);\n \t\thsr-\u003esequence_nr++;\n \t}\n+\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\n \n \thsr_stag-\u003etlv.HSR_TLV_type = type;\n \t/* HSRv0 has 6 unused bytes after the MAC */\n@@ -356,14 +355,10 @@ static void send_hsr_supervision_frame(struct hsr_port *port,\n \t\tether_addr_copy(hsr_sp-\u003emacaddress_A, hsr-\u003emacaddress_redbox);\n \t}\n \n-\tif (skb_put_padto(skb, ETH_ZLEN)) {\n-\t\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\n+\tif (skb_put_padto(skb, ETH_ZLEN))\n \t\treturn;\n-\t}\n \n \thsr_forward_skb(skb, port);\n-\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\n-\treturn;\n }\n \n static void send_prp_supervision_frame(struct hsr_port *master,\n@@ -390,6 +385,7 @@ static void send_prp_supervision_frame(struct hsr_port *master,\n \tspin_lock_bh(\u0026hsr-\u003eseqnr_lock);\n \thsr_stag-\u003esequence_nr = htons(hsr-\u003esup_sequence_nr);\n \thsr-\u003esup_sequence_nr++;\n+\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\n \thsr_stag-\u003etlv.HSR_TLV_type = PRP_TLV_LIFE_CHECK_DD;\n \thsr_stag-\u003etlv.HSR_TLV_length = sizeof(struct hsr_sup_payload);\n \n@@ -397,13 +393,10 @@ static void send_prp_supervision_frame(struct hsr_port *master,\n \thsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload));\n \tether_addr_copy(hsr_sp-\u003emacaddress_A, master-\u003edev-\u003edev_addr);\n \n-\tif (skb_put_padto(skb, ETH_ZLEN)) {\n-\t\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\n+\tif (skb_put_padto(skb, ETH_ZLEN))\n \t\treturn;\n-\t}\n \n \thsr_forward_skb(skb, master);\n-\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\n }\n \n /* Announce (supervision frame) timer function\n@@ -659,7 +652,7 @@ void hsr_dev_setup(struct net_device *dev)\n \tdev-\u003eneeds_free_netdev = true;\n \n \tdev-\u003ehw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |\n-\t\t\t NETIF_F_GSO_MASK | NETIF_F_HW_CSUM |\n+\t\t\t NETIF_F_HW_CSUM |\n \t\t\t NETIF_F_HW_VLAN_CTAG_TX |\n \t\t\t NETIF_F_HW_VLAN_CTAG_FILTER;\n \ndiff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c\nindex 0774981a65c16..3fcdbac49c59d 100644\n--- a/net/hsr/hsr_forward.c\n+++ b/net/hsr/hsr_forward.c\n@@ -12,6 +12,7 @@\n #include \u003clinux/skbuff.h\u003e\n #include \u003clinux/etherdevice.h\u003e\n #include \u003clinux/if_vlan.h\u003e\n+#include \u003cnet/gso.h\u003e\n #include \"hsr_main.h\"\n #include \"hsr_framereg.h\"\n \n@@ -621,9 +622,10 @@ static void handle_std_frame(struct sk_buff *skb,\n \tif (port-\u003etype == HSR_PT_MASTER ||\n \t port-\u003etype == HSR_PT_INTERLINK) {\n \t\t/* Sequence nr for the master/interlink node */\n-\t\tlockdep_assert_held(\u0026hsr-\u003eseqnr_lock);\n+\t\tspin_lock_bh(\u0026hsr-\u003eseqnr_lock);\n \t\tframe-\u003esequence_nr = hsr-\u003esequence_nr;\n \t\thsr-\u003esequence_nr++;\n+\t\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\n \t}\n }\n \n@@ -731,7 +733,7 @@ static int fill_frame_info(struct hsr_frame_info *frame,\n }\n \n /* Must be called holding rcu read lock (because of the port parameter) */\n-void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)\n+static void hsr_forward_skb_one(struct sk_buff *skb, struct hsr_port *port)\n {\n \tstruct hsr_frame_info frame;\n \n@@ -760,3 +762,50 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)\n \tport-\u003edev-\u003estats.tx_dropped++;\n \tkfree_skb(skb);\n }\n+\n+/* GSO fan-out funnel: unfold super-packets before per-frame processing so\n+ * each wire frame gets its own HSR/PRP tag and sequence number.\n+ */\n+void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)\n+{\n+\tstruct sk_buff *segs, *next;\n+\n+\tif (likely(!skb_is_gso(skb))) {\n+\t\thsr_forward_skb_one(skb, port);\n+\t\treturn;\n+\t}\n+\n+\t/* Unfold only plain-Ethernet GSO super-packets: locally generated\n+\t * on the master, or arriving untagged from the SAN side on the\n+\t * interlink. A super-packet from a LAN slave may carry per-frame\n+\t * HSR tags / PRP RCT trailers that software segmentation cannot\n+\t * recover; an already-tagged HSR/PRP super-packet violates\n+\t * per-frame wire semantics. Drop both.\n+\t */\n+\tif (port-\u003etype != HSR_PT_MASTER \u0026\u0026 port-\u003etype != HSR_PT_INTERLINK)\n+\t\tgoto drop_gso;\n+\tif (skb-\u003eprotocol == htons(ETH_P_HSR) ||\n+\t skb-\u003eprotocol == htons(ETH_P_PRP))\n+\t\tgoto drop_gso;\n+\n+\t/* features = 0: request full software segmentation. tx_path is true\n+\t * only for locally generated traffic on the master; ingress from\n+\t * the interlink follows RX checksum semantics.\n+\t */\n+\tsegs = __skb_gso_segment(skb, 0, port-\u003etype == HSR_PT_MASTER);\n+\tif (IS_ERR(segs) || unlikely(!segs))\n+\t\tgoto drop_gso;\n+\n+\tconsume_skb(skb);\n+\twhile (segs) {\n+\t\tnext = segs-\u003enext;\n+\t\tsegs-\u003enext = NULL;\n+\t\thsr_forward_skb_one(segs, port);\n+\t\tsegs = next;\n+\t}\n+\treturn;\n+\n+drop_gso:\n+\tport-\u003edev-\u003estats.tx_dropped++;\n+\tkfree_skb(skb);\n+}\ndiff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c\nindex 01c73b4b50ddd..0ca55d9323c57 100644\n--- a/net/hsr/hsr_slave.c\n+++ b/net/hsr/hsr_slave.c\n@@ -73,16 +73,7 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)\n \t}\n \tskb_reset_mac_len(skb);\n \n-\t/* Only the frames received over the interlink port will assign a\n-\t * sequence number and require synchronisation vs other sender.\n-\t */\n-\tif (port-\u003etype == HSR_PT_INTERLINK) {\n-\t\tspin_lock_bh(\u0026hsr-\u003eseqnr_lock);\n-\t\thsr_forward_skb(skb, port);\n-\t\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\n-\t} else {\n-\t\thsr_forward_skb(skb, port);\n-\t}\n+\thsr_forward_skb(skb, port);\n \n finish_consume:\n \treturn RX_HANDLER_CONSUMED;\n@@ -170,6 +161,7 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,\n \tif (res)\n \t\tgoto fail_rx_handler;\n \tdev_disable_lro(dev);\n+\tdev_disable_gro(dev);\n \n \treturn 0;\n \ndiff --git a/tools/testing/selftests/net/hsr/Makefile b/tools/testing/selftests/net/hsr/Makefile\nindex 31fb9326cf533..0d105476e7c50 100644\n--- a/tools/testing/selftests/net/hsr/Makefile\n+++ b/tools/testing/selftests/net/hsr/Makefile\n@@ -3,6 +3,7 @@\n top_srcdir = ../../../../..\n \n TEST_PROGS := \\\n+\thsr_gro_superpacket.sh \\\n \thsr_ping.sh \\\n \thsr_redbox.sh \\\n \tlink_faults.sh \\\ndiff --git a/tools/testing/selftests/net/hsr/hsr_gro_superpacket.sh b/tools/testing/selftests/net/hsr/hsr_gro_superpacket.sh\nnew file mode 100755\nindex 0000000000000..288de3a60b899\n--- /dev/null\n+++ b/tools/testing/selftests/net/hsr/hsr_gro_superpacket.sh\n@@ -0,0 +1,462 @@\n+#!/bin/bash\n+# SPDX-License-Identifier: GPL-2.0\n+#\n+# Test HSR handling of GRO/GSO super-packets:\n+#\n+# 1. Enslaving a device to an HSR master disables GRO on it\n+# (dev_disable_gro()).\n+# 2. The HSR master does not advertise GSO/TSO features.\n+# 3. A TCP stream from a TSO-enabled SAN (which therefore emits GSO\n+# super-packets) is unfolded at the HSR forward entry. Evidence:\n+# interface-counter deltas show super-packet-sized frames leaving\n+# the SAN and per-frame-sized traffic leaving the DUT's LAN ports.\n+#\n+# Topology (100.64.0.0/24):\n+#\n+# ns_san ns_dut ns_peer\n+# +-----------+ interlink +---------------+ LAN A/B +-----------+\n+# | s0 [0.1] |-------------| d_il hsr0 |-----------| hsr1 [0.3]|\n+# +-----------+ | d_a / d_b | | p_a / p_b |\n+# +---------------+ +-----------+\n+#\n+# SAN traffic reaches ns_peer only through hsr0's forward path\n+# (interlink RX -\u003e LAN A/B TX), so every SAN frame is tagged and\n+# forwarded by the DUT.\n+\n+source ./hsr_common.sh\n+\n+san_ip=\"100.64.0.1\"\n+peer_ip=\"100.64.0.3\"\n+\n+# Aggregate counter thresholds for the stream test (bytes/packets):\n+# SAN_AVG_MIN proves GSO super-packets left the SAN; LAN_AVG_MAX is a\n+# guard with margin, not the protocol maximum (see do_tso_stream_test).\n+SAN_AVG_MIN=2048\n+LAN_AVG_MAX=1514\n+\n+iperf_pid=\"\"\n+server_wrapper=\"\"\n+workdir=\"\"\n+pidfile=\"\"\n+rcfile=\"\"\n+\n+cleanup()\n+{\n+\t# exact-PID kill only after RE-validating identity (guards against\n+\t# PID reuse between publication and cleanup)\n+\tif [ -n \"${iperf_pid}\" ] \u0026\u0026 valid_server_pid \"${iperf_pid}\"; then\n+\t\tkill \"${iperf_pid}\" 2\u003e/dev/null\n+\tfi\n+\tiperf_pid=\"\"\n+\tif [ -n \"${server_wrapper}\" ]; then\n+\t\t# the wrapper waits on the server; reap it with a 5s bound so a\n+\t\t# live-but-unpublished server can never hang cleanup\n+\t\tfor _ in $(seq 1 50); do\n+\t\t\tkill -0 \"${server_wrapper}\" 2\u003e/dev/null || break\n+\t\t\tsleep 0.1\n+\t\tdone\n+\t\tkill \"${server_wrapper}\" 2\u003e/dev/null\n+\t\twait \"${server_wrapper}\" 2\u003e/dev/null\n+\t\tserver_wrapper=\"\"\n+\tfi\n+\t# last resort, namespace-scoped only: TERM the iperf3 processes that\n+\t# actually live in the peer netns, poll for bounded exit, then\n+\t# SIGKILL any survivor before touching the namespace name. A blind\n+\t# pkill would scan the host PID space and hit unrelated tests.\n+\tlocal _p _still\n+\tfor _p in $(ip netns pids \"$ns_peer\" 2\u003e/dev/null); do\n+\t\tif is_iperf3_pid \"$_p\"; then\n+\t\t\tkill \"$_p\" 2\u003e/dev/null\n+\t\tfi\n+\tdone\n+\tfor _ in $(seq 1 50); do\n+\t\t_still=0\n+\t\tfor _p in $(ip netns pids \"$ns_peer\" 2\u003e/dev/null); do\n+\t\t\tif is_iperf3_pid \"$_p\"; then\n+\t\t\t\t_still=1\n+\t\t\t\tbreak\n+\t\t\tfi\n+\t\tdone\n+\t\t[ \"$_still\" -eq 0 ] \u0026\u0026 break\n+\t\tsleep 0.1\n+\tdone\n+\tfor _p in $(ip netns pids \"$ns_peer\" 2\u003e/dev/null); do\n+\t\tif is_iperf3_pid \"$_p\"; then\n+\t\t\tkill -9 \"$_p\" 2\u003e/dev/null\n+\t\tfi\n+\tdone\n+\t# remove only the known non-empty private directory\n+\tif [ -n \"${workdir}\" ] \u0026\u0026 [ -d \"${workdir}\" ]; then\n+\t\trm -rf \"${workdir}\"\n+\tfi\n+\tworkdir=\"\"\n+\tpidfile=\"\"\n+\trcfile=\"\"\n+\tcleanup_all_ns\n+}\n+\n+trap cleanup EXIT\n+\n+check_tool()\n+{\n+\tif ! command -v \"$1\" \u003e /dev/null 2\u003e\u00261; then\n+\t\techo \"SKIP: Could not run test without $1\"\n+\t\texit $ksft_skip\n+\tfi\n+}\n+\n+nsx()\n+{\n+\tip netns exec \"$1\" bash -c \"$2\"\n+}\n+\n+is_iperf3_pid()\n+{\n+\t[ \"$(cat /proc/\"$1\"/comm 2\u003e/dev/null)\" = \"iperf3\" ]\n+}\n+\n+# Decimal-counter validation for the snapshot blocks: every value must\n+# be a plain decimal number. A parse failure in read_tx_counters yields\n+# empty/garbled fields, which this check turns into an immediate FAIL.\n+valid_decimals()\n+{\n+\tlocal v\n+\n+\tfor v in \"$@\"; do\n+\t\t[[ \"$v\" =~ ^[0-9]+$ ]] || return 1\n+\tdone\n+\treturn 0\n+}\n+\n+setup_topo()\n+{\n+\tsetup_ns ns_dut ns_san ns_peer || exit $?\n+\n+\tip link add d_a netns \"$ns_dut\" type veth peer name p_a netns \"$ns_peer\"\n+\tip link add d_b netns \"$ns_dut\" type veth peer name p_b netns \"$ns_peer\"\n+\tip link add d_il netns \"$ns_dut\" type veth peer name s0 netns \"$ns_san\"\n+\n+\t# HSR tags add 6 bytes per frame; give the LAN legs headroom.\n+\tfor iface in d_a d_b; do\n+\t\tnsx \"$ns_dut\" \"ip link set $iface mtu 1600; \\\n+\t\t\tip link set $iface up\"\n+\tdone\n+\tfor iface in p_a p_b; do\n+\t\tnsx \"$ns_peer\" \"ip link set $iface mtu 1600; \\\n+\t\t\tip link set $iface up\"\n+\tdone\n+\n+\tnsx \"$ns_dut\" \"ip link set d_il up\"\n+\tnsx \"$ns_san\" \"ip link set s0 up; ip addr add $san_ip/24 dev s0\"\n+\n+\tnsx \"$ns_dut\" \"ip link add hsr0 type hsr \\\n+\t\tslave1 d_a slave2 d_b interlink d_il proto 0; \\\n+\t\tip link set hsr0 up\"\n+\tnsx \"$ns_peer\" \"ip link add hsr1 type hsr \\\n+\t\tslave1 p_a slave2 p_b proto 0; \\\n+\t\tip link set hsr1 up; ip addr add $peer_ip/24 dev hsr1\"\n+\n+\t# Let the nodes see each other's supervision frames.\n+\tsleep 2\n+}\n+\n+check_feature()\n+{\n+\tlocal ns=\"$1\"\n+\tlocal iface=\"$2\"\n+\tlocal feature=\"$3\"\n+\tlocal want=\"$4\"\n+\n+\tif nsx \"$ns\" \"ethtool -k $iface\" | grep -q \"^$feature: $want\"; then\n+\t\techo \"INFO: $ns/$iface $feature is $want [ OK ]\"\n+\telse\n+\t\techo \"FAIL: $ns/$iface $feature is not $want\" 1\u003e\u00262\n+\t\tret=1\n+\tfi\n+}\n+\n+# Off-or-absent variant: fails only when the feature is present AND on,\n+# so devices that simply do not list the feature do not fail it.\n+check_feature_not_on()\n+{\n+\tlocal ns=\"$1\"\n+\tlocal iface=\"$2\"\n+\tlocal feature=\"$3\"\n+\n+\tif nsx \"$ns\" \"ethtool -k $iface\" | grep -q \"^$feature: on\"; then\n+\t\techo \"FAIL: $ns/$iface $feature is on\" 1\u003e\u00262\n+\t\tret=1\n+\telse\n+\t\techo \"INFO: $ns/$iface $feature not on [ OK ]\"\n+\tfi\n+}\n+\n+do_gro_feature_checks()\n+{\n+\techo \"INFO: Checking that enslavement disabled GRO.\"\n+\tcheck_feature \"$ns_dut\" d_a generic-receive-offload off\n+\tcheck_feature \"$ns_dut\" d_b generic-receive-offload off\n+\tcheck_feature \"$ns_dut\" d_il generic-receive-offload off\n+\tstop_if_error \"GRO not disabled on enslaved devices.\"\n+\n+\techo \"INFO: Checking that enslavement disabled HW-GRO.\"\n+\tcheck_feature \"$ns_dut\" d_a rx-gro-hw off\n+\tcheck_feature \"$ns_dut\" d_b rx-gro-hw off\n+\tcheck_feature \"$ns_dut\" d_il rx-gro-hw off\n+\tstop_if_error \"HW-GRO not disabled on enslaved devices.\"\n+\n+\techo \"INFO: Checking that the HSR master does not advertise GSO/TSO.\"\n+\tcheck_feature \"$ns_dut\" hsr0 generic-segmentation-offload off\n+\tcheck_feature \"$ns_dut\" hsr0 tcp-segmentation-offload off\n+\tcheck_feature_not_on \"$ns_dut\" hsr0 tx-tcp6-segmentation\n+\tcheck_feature_not_on \"$ns_dut\" hsr0 tx-udp-segmentation\n+\tcheck_feature_not_on \"$ns_dut\" hsr0 tx-gso-list\n+\tstop_if_error \"HSR master still advertises GSO-family features.\"\n+}\n+\n+alloc_workdir()\n+{\n+\t# Allocated only here, long after the initial topology cleanup, so\n+\t# cleanup() at setup_topo() time can never remove it. mktemp failure\n+\t# is a hard test failure.\n+\tworkdir=$(mktemp -d /tmp/hsr_gro_test.XXXXXX) || {\n+\t\techo \"FAIL: mktemp -d failed\" 1\u003e\u00262\n+\t\texit 1\n+\t}\n+\tchmod 700 \"${workdir}\"\n+\tpidfile=\"${workdir}/iperf.pid\"\n+\trcfile=\"${workdir}/iperf.rc\"\n+}\n+\n+# Numeric, alive, comm == iperf3, and really owned by the peer netns.\n+valid_server_pid()\n+{\n+\tlocal p=\"$1\"\n+\n+\t[[ \"$p\" =~ ^[0-9]+$ ]] || return 1\n+\tkill -0 \"$p\" 2\u003e/dev/null || return 1\n+\t[ \"$(cat /proc/\"$p\"/comm 2\u003e/dev/null)\" = \"iperf3\" ] || return 1\n+\tip netns pids \"$ns_peer\" 2\u003e/dev/null | grep -qx \"$p\"\n+}\n+\n+start_iperf_server()\n+{\n+\tlocal candidate_pid\n+\n+\t# One-shot server, no -D: the wrapper records its exact PID and its\n+\t# real exit status (netns shares the PID namespace and the host fs).\n+\talloc_workdir\n+\t( nsx \"$ns_peer\" \"iperf3 -s -1 \u003e /dev/null 2\u003e\u00261 \u0026 \\\n+\t\techo \\$! \u003e ${pidfile}; \\\n+\t\twait \\$!; \\\n+\t\techo \\$? \u003e ${rcfile}\" ) \u0026\n+\tserver_wrapper=$!\n+\t# the wrapper writes the pidfile asynchronously; wait for it to\n+\t# appear instead of racing the read\n+\tfor _ in $(seq 1 50); do\n+\t\t[ -s \"${pidfile}\" ] \u0026\u0026 break\n+\t\tsleep 0.1\n+\tdone\n+\tif [ ! -s \"${pidfile}\" ]; then\n+\t\techo \"FAIL: iperf3 server did not publish a pid\" \\\n+\t\t\t\"(no pidfile)\" 1\u003e\u00262\n+\t\tret=1\n+\t\treturn 1\n+\tfi\n+\tcandidate_pid=$(\u003c\"${pidfile}\")\n+\tif ! valid_server_pid \"${candidate_pid}\"; then\n+\t\techo \"FAIL: iperf3 server pid '${candidate_pid}'\" \\\n+\t\t\t\"failed validation\" 1\u003e\u00262\n+\t\tret=1\n+\t\treturn 1\n+\tfi\n+\t# publish only after full validation\n+\tiperf_pid=\"${candidate_pid}\"\n+\tsleep 1\n+\treturn 0\n+}\n+\n+# Print \"\u003cbytes\u003e \u003cpackets\u003e\" for exactly one TX record of ns/dev; anything\n+# else (missing, duplicated, non-numeric) is a hard FAIL.\n+read_tx_counters()\n+{\n+\tlocal ns=\"$1\" dev=\"$2\"\n+\tlocal out cnt\n+\n+\tout=$(nsx \"$ns\" \"ip -s link show $dev\" | \\\n+\t\tawk '/^ +TX:/{getline; print $1, $2}')\n+\tcnt=$(echo \"$out\" | grep -c '^[0-9]* [0-9]*$')\n+\tif [ \"$cnt\" -ne 1 ]; then\n+\t\techo \"FAIL: cannot parse TX counters of $ns/$dev\" \\\n+\t\t\t\"(records=$cnt)\" 1\u003e\u00262\n+\t\treturn 1\n+\tfi\n+\techo \"$out\"\n+\treturn 0\n+}\n+\n+eval_counter_delta()\n+{\n+\tlocal name=\"$1\" b0=\"$2\" p0=\"$3\" b1=\"$4\" p1=\"$5\" op=\"$6\" limit=\"$7\"\n+\tlocal bd pd\n+\n+\tif ! [[ \"$b0\" =~ ^[0-9]+$ \u0026\u0026 \"$b1\" =~ ^[0-9]+$ \u0026\u0026 \\\n+\t\t\"$p0\" =~ ^[0-9]+$ \u0026\u0026 \"$p1\" =~ ^[0-9]+$ ]]; then\n+\t\techo \"FAIL: non-numeric counter input for $name\" 1\u003e\u00262\n+\t\tret=1\n+\t\treturn 1\n+\tfi\n+\tbd=$((b1 - b0))\n+\tpd=$((p1 - p0))\n+\tif [ \"$bd\" -lt 0 ] || [ \"$pd\" -le 0 ]; then\n+\t\techo \"FAIL: counter delta invalid for $name\" \\\n+\t\t\t\"(bytes=$bd pkts=$pd)\" 1\u003e\u00262\n+\t\tret=1\n+\t\treturn 1\n+\tfi\n+\tif [ \"$op\" = \"gt\" ]; then\n+\t\tif [ \"$bd\" -le $((pd * limit)) ]; then\n+\t\t\techo \"FAIL: $name bytes/packets $bd/$pd \u003c= $limit\" 1\u003e\u00262\n+\t\t\tret=1\n+\t\t\treturn 1\n+\t\tfi\n+\telse\n+\t\tif [ \"$bd\" -gt $((pd * limit)) ]; then\n+\t\t\techo \"FAIL: $name bytes/packets $bd/$pd \u003e $limit\" 1\u003e\u00262\n+\t\t\tret=1\n+\t\t\treturn 1\n+\t\tfi\n+\tfi\n+\techo \"INFO: $name counter delta bytes=$bd packets=$pd\" \\\n+\t\t\"(op $op limit $limit) [ OK ]\"\n+\treturn 0\n+}\n+\n+do_tso_stream_test()\n+{\n+\tlocal out sender_retr server_rc\n+\tlocal san_b0 san_p0 san_b1 san_p1\n+\tlocal a_b0 a_p0 a_b1 a_p1 b_b0 b_p0 b_b1 b_p1\n+\n+\techo \"INFO: Enabling TSO/GSO on the SAN interface.\"\n+\tnsx \"$ns_san\" \"ethtool -K s0 tso on gso on\"\n+\tcheck_feature \"$ns_san\" s0 tcp-segmentation-offload on\n+\tstop_if_error \"Could not enable TSO on the SAN interface.\"\n+\n+\techo \"INFO: Running 10s TCP stream SAN -\u003e peer through the HSR DUT.\"\n+\tstart_iperf_server || return\n+\n+\t# Counter snapshots around the stream window. The SAN-side average\n+\t# must exceed SAN_AVG_MIN (aggregate proof that GSO super-packets\n+\t# really left the SAN); each DUT LAN leg must stay under LAN_AVG_MAX\n+\t# (aggregate proof that bulk output was segmented per-frame). These\n+\t# are aggregate discriminators, not a per-frame maximum proof.\n+\tsan_b0=0; san_p0=0; a_b0=0; a_p0=0; b_b0=0; b_p0=0\n+\tread -r san_b0 san_p0 \u003c\u003cEOF\n+$(read_tx_counters \"$ns_san\" s0)\n+EOF\n+\tread -r a_b0 a_p0 \u003c\u003cEOF\n+$(read_tx_counters \"$ns_dut\" d_a)\n+EOF\n+\tread -r b_b0 b_p0 \u003c\u003cEOF\n+$(read_tx_counters \"$ns_dut\" d_b)\n+EOF\n+\tif ! valid_decimals \"$san_b0\" \"$san_p0\" \"$a_b0\" \"$a_p0\" \\\n+\t\t\"$b_b0\" \"$b_p0\"; then\n+\t\techo \"FAIL: baseline TX counter snapshot invalid\" 1\u003e\u00262\n+\t\tret=1\n+\t\treturn 1\n+\tfi\n+\n+\t# rate-capped: the PRIMARY discriminator is the counter inequality\n+\t# above, not max throughput; retransmits are informational only.\n+\t# Uncapped runs flap at VM/CI edge rates without indicating a\n+\t# functional problem.\n+\tif ! out=$(nsx \"$ns_san\" \"timeout 60 iperf3 -c $peer_ip -M 1446 \\\n+\t\t-b 2G -t 10\" 2\u003e\u00261); then\n+\t\techo \"FAIL: iperf3 client failed:\" 1\u003e\u00262\n+\t\techo \"$out\" 1\u003e\u00262\n+\t\tret=1\n+\t\treturn\n+\tfi\n+\n+\tread -r san_b1 san_p1 \u003c\u003cEOF\n+$(read_tx_counters \"$ns_san\" s0)\n+EOF\n+\tread -r a_b1 a_p1 \u003c\u003cEOF\n+$(read_tx_counters \"$ns_dut\" d_a)\n+EOF\n+\tread -r b_b1 b_p1 \u003c\u003cEOF\n+$(read_tx_counters \"$ns_dut\" d_b)\n+EOF\n+\tif ! valid_decimals \"$san_b1\" \"$san_p1\" \"$a_b1\" \"$a_p1\" \\\n+\t\t\"$b_b1\" \"$b_p1\"; then\n+\t\techo \"FAIL: final TX counter snapshot invalid\" 1\u003e\u00262\n+\t\tret=1\n+\t\treturn 1\n+\tfi\n+\n+\teval_counter_delta \"SAN s0 TX\" \"$san_b0\" \"$san_p0\" \"$san_b1\" \"$san_p1\" \\\n+\t\tgt \"$SAN_AVG_MIN\"\n+\teval_counter_delta \"DUT d_a TX\" \"$a_b0\" \"$a_p0\" \"$a_b1\" \"$a_p1\" \\\n+\t\tle \"$LAN_AVG_MAX\"\n+\teval_counter_delta \"DUT d_b TX\" \"$b_b0\" \"$b_p0\" \"$b_b1\" \"$b_p1\" \\\n+\t\tle \"$LAN_AVG_MAX\"\n+\t[ \"${ret:-0}\" -eq 0 ] || return\n+\n+\t# success path: the one-shot server exits by itself; reap the\n+\t# wrapper, then REQUIRE the rcfile with the server's real status\n+\twait \"${server_wrapper}\"\n+\tserver_wrapper=\"\"\n+\tif [ ! -s \"${rcfile}\" ]; then\n+\t\techo \"FAIL: iperf3 server status file missing (${rcfile})\" 1\u003e\u00262\n+\t\tret=1\n+\t\treturn\n+\tfi\n+\tserver_rc=$(cat \"${rcfile}\")\n+\tif ! [[ \"$server_rc\" =~ ^[0-9]+$ ]] || [ \"$server_rc\" -ne 0 ]; then\n+\t\techo \"FAIL: iperf3 server exited with rc='${server_rc}'\" 1\u003e\u00262\n+\t\tret=1\n+\t\treturn\n+\tfi\n+\tiperf_pid=\"\"\n+\n+\t# secondary health signal only: anchored, single-match, numeric —\n+\t# any parse anomaly is a loud FAIL, but the value itself no longer\n+\t# gates (the counter inequalities above are the primary evidence).\n+\tsender_retr=$(echo \"$out\" | awk '/sec .* sender$/ {print $(NF-1)}')\n+\tif [ \"$(echo \"$sender_retr\" | grep -Ec '^[0-9]+$')\" -ne 1 ]; then\n+\t\techo \"FAIL: cannot parse sender retransmits reliably\" 1\u003e\u00262\n+\t\techo \"$out\" 1\u003e\u00262\n+\t\tret=1\n+\t\treturn\n+\tfi\n+\techo \"INFO: TCP stream done;\" \\\n+\t\t\"sender retransmits=$sender_retr (secondary signal)\"\n+\techo \"$out\" | grep -E \"sender|receiver\"\n+}\n+\n+check_prerequisites\n+check_tool ethtool\n+check_tool iperf3\n+check_tool timeout\n+\n+# iproute2 must know the HSR interlink syntax.\n+if ! ip link help hsr 2\u003e\u00261 | grep -qi interlink; then\n+\techo \"SKIP: iproute2 has no HSR interlink support\"\n+\texit $ksft_skip\n+fi\n+\n+setup_topo\n+\n+echo \"INFO: Initial validation ping (SAN -\u003e peer through the DUT).\"\n+do_ping \"$ns_san\" \"$peer_ip\"\n+stop_if_error \"Initial validation failed.\"\n+\n+do_gro_feature_checks\n+do_tso_stream_test\n+stop_if_error \"GSO super-packet stream test failed.\"\n+\n+echo \"INFO: All good.\"\n+cleanup\n+exit $ret\n"
} |
| 2/1 | 2026/08/01 15:46 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"HSR"
],
"FocusSymbols": [
"hsr_forward_skb",
"dev_disable_gro",
"netif_disable_gro",
"hsr_dev_xmit",
"hsr_handle_frame"
],
"Reasoning": "The patch adds `dev_disable_gro` and `netif_disable_gro` to disable GRO on net devices, and modifies the HSR driver to unfold GSO super-packets in `hsr_forward_skb`. It also disables GRO on HSR slave devices. These are functional changes to core networking and the HSR driver that can be reached by sending traffic through an HSR interface, making them highly relevant for fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 8cf755a9d92b390817960e73fe82736d0f40ad79
Author: syz-cluster <triage@syzkaller.com>
Date: Sat Aug 1 15:46:28 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 9981d637f8b54..eba2c26a49ba8 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3434,6 +3434,8 @@ void dev_close(struct net_device *dev);
void netif_close_many(struct list_head *head, bool unlink);
void netif_disable_lro(struct net_device *dev);
void dev_disable_lro(struct net_device *dev);
+void netif_disable_gro(struct net_device *dev);
+void dev_disable_gro(struct net_device *dev);
int dev_loopback_xmit(struct net *net, struct sock *sk, struct sk_buff *newskb);
u16 dev_pick_tx_zero(struct net_device *dev, struct sk_buff *skb,
struct net_device *sb_dev);
diff --git a/net/core/dev.c b/net/core/dev.c
index 5933c5dab09ee..a6cf2adc86256 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1840,6 +1840,24 @@ void netif_disable_lro(struct net_device *dev)
}
}
+void netif_disable_gro(struct net_device *dev)
+{
+ struct net_device *lower_dev;
+ struct list_head *iter;
+
+ dev->wanted_features &= ~(NETIF_F_GRO | NETIF_F_GRO_HW);
+ netdev_update_features(dev);
+
+ if (unlikely(dev->features & (NETIF_F_GRO | NETIF_F_GRO_HW)))
+ netdev_WARN(dev, "failed to disable GRO!\n");
+
+ netdev_for_each_lower_dev(dev, lower_dev, iter) {
+ netdev_lock_ops(lower_dev);
+ netif_disable_gro(lower_dev);
+ netdev_unlock_ops(lower_dev);
+ }
+}
+
/**
* dev_disable_gro_hw - disable HW Generic Receive Offload on a device
* @dev: device
diff --git a/net/core/dev_api.c b/net/core/dev_api.c
index 437947dd08ed5..02fb216295128 100644
--- a/net/core/dev_api.c
+++ b/net/core/dev_api.c
@@ -269,6 +269,22 @@ void dev_disable_lro(struct net_device *dev)
}
EXPORT_SYMBOL(dev_disable_lro);
+/**
+ * dev_disable_gro() - disable Generic Receive Offload on a device
+ * @dev: device
+ *
+ * Disable Generic Receive Offload (GRO) on a net device. Must be
+ * called under RTNL. This is needed if received packets may be
+ * forwarded to another interface.
+ */
+void dev_disable_gro(struct net_device *dev)
+{
+ netdev_lock_ops(dev);
+ netif_disable_gro(dev);
+ netdev_unlock_ops(dev);
+}
+EXPORT_SYMBOL(dev_disable_gro);
+
/**
* dev_set_promiscuity() - update promiscuity count on a device
* @dev: device
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
index 5555b71ab19b5..248cbb142e21e 100644
--- a/net/hsr/hsr_device.c
+++ b/net/hsr/hsr_device.c
@@ -232,9 +232,7 @@ static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
skb->dev = master->dev;
skb_reset_mac_header(skb);
skb_reset_mac_len(skb);
- spin_lock_bh(&hsr->seqnr_lock);
hsr_forward_skb(skb, master);
- spin_unlock_bh(&hsr->seqnr_lock);
} else {
dev_core_stats_tx_dropped_inc(dev);
dev_kfree_skb_any(skb);
@@ -335,6 +333,7 @@ static void send_hsr_supervision_frame(struct hsr_port *port,
hsr_stag->sequence_nr = htons(hsr->sequence_nr);
hsr->sequence_nr++;
}
+ spin_unlock_bh(&hsr->seqnr_lock);
hsr_stag->tlv.HSR_TLV_type = type;
/* HSRv0 has 6 unused bytes after the MAC */
@@ -356,14 +355,10 @@ static void send_hsr_supervision_frame(struct hsr_port *port,
ether_addr_copy(hsr_sp->macaddress_A, hsr->macaddress_redbox);
}
- if (skb_put_padto(skb, ETH_ZLEN)) {
- spin_unlock_bh(&hsr->seqnr_lock);
+ if (skb_put_padto(skb, ETH_ZLEN))
return;
- }
hsr_forward_skb(skb, port);
- spin_unlock_bh(&hsr->seqnr_lock);
- return;
}
static void send_prp_supervision_frame(struct hsr_port *master,
@@ -390,6 +385,7 @@ static void send_prp_supervision_frame(struct hsr_port *master,
spin_lock_bh(&hsr->seqnr_lock);
hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr);
hsr->sup_sequence_nr++;
+ spin_unlock_bh(&hsr->seqnr_lock);
hsr_stag->tlv.HSR_TLV_type = PRP_TLV_LIFE_CHECK_DD;
hsr_stag->tlv.HSR_TLV_length = sizeof(struct hsr_sup_payload);
@@ -397,13 +393,10 @@ static void send_prp_supervision_frame(struct hsr_port *master,
hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload));
ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr);
- if (skb_put_padto(skb, ETH_ZLEN)) {
- spin_unlock_bh(&hsr->seqnr_lock);
+ if (skb_put_padto(skb, ETH_ZLEN))
return;
- }
hsr_forward_skb(skb, master);
- spin_unlock_bh(&hsr->seqnr_lock);
}
/* Announce (supervision frame) timer function
@@ -659,7 +652,7 @@ void hsr_dev_setup(struct net_device *dev)
dev->needs_free_netdev = true;
dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
- NETIF_F_GSO_MASK | NETIF_F_HW_CSUM |
+ NETIF_F_HW_CSUM |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_CTAG_FILTER;
diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index 0774981a65c16..3fcdbac49c59d 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -12,6 +12,7 @@
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
+#include <net/gso.h>
#include "hsr_main.h"
#include "hsr_framereg.h"
@@ -621,9 +622,10 @@ static void handle_std_frame(struct sk_buff *skb,
if (port->type == HSR_PT_MASTER ||
port->type == HSR_PT_INTERLINK) {
/* Sequence nr for the master/interlink node */
- lockdep_assert_held(&hsr->seqnr_lock);
+ spin_lock_bh(&hsr->seqnr_lock);
frame->sequence_nr = hsr->sequence_nr;
hsr->sequence_nr++;
+ spin_unlock_bh(&hsr->seqnr_lock);
}
}
@@ -731,7 +733,7 @@ static int fill_frame_info(struct hsr_frame_info *frame,
}
/* Must be called holding rcu read lock (because of the port parameter) */
-void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
+static void hsr_forward_skb_one(struct sk_buff *skb, struct hsr_port *port)
{
struct hsr_frame_info frame;
@@ -760,3 +762,50 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
port->dev->stats.tx_dropped++;
kfree_skb(skb);
}
+
+/* GSO fan-out funnel: unfold super-packets before per-frame processing so
+ * each wire frame gets its own HSR/PRP tag and sequence number.
+ */
+void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
+{
+ struct sk_buff *segs, *next;
+
+ if (likely(!skb_is_gso(skb))) {
+ hsr_forward_skb_one(skb, port);
+ return;
+ }
+
+ /* Unfold only plain-Ethernet GSO super-packets: locally generated
+ * on the master, or arriving untagged from the SAN side on the
+ * interlink. A super-packet from a LAN slave may carry per-frame
+ * HSR tags / PRP RCT trailers that software segmentation cannot
+ * recover; an already-tagged HSR/PRP super-packet violates
+ * per-frame wire semantics. Drop both.
+ */
+ if (port->type != HSR_PT_MASTER && port->type != HSR_PT_INTERLINK)
+ goto drop_gso;
+ if (skb->protocol == htons(ETH_P_HSR) ||
+ skb->protocol == htons(ETH_P_PRP))
+ goto drop_gso;
+
+ /* features = 0: request full software segmentation. tx_path is true
+ * only for locally generated traffic on the master; ingress from
+ * the interlink follows RX checksum semantics.
+ */
+ segs = __skb_gso_segment(skb, 0, port->type == HSR_PT_MASTER);
+ if (IS_ERR(segs) || unlikely(!segs))
+ goto drop_gso;
+
+ consume_skb(skb);
+ while (segs) {
+ next = segs->next;
+ segs->next = NULL;
+ hsr_forward_skb_one(segs, port);
+ segs = next;
+ }
+ return;
+
+drop_gso:
+ port->dev->stats.tx_dropped++;
+ kfree_skb(skb);
+}
diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c
index 01c73b4b50ddd..0ca55d9323c57 100644
--- a/net/hsr/hsr_slave.c
+++ b/net/hsr/hsr_slave.c
@@ -73,16 +73,7 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
}
skb_reset_mac_len(skb);
- /* Only the frames received over the interlink port will assign a
- * sequence number and require synchronisation vs other sender.
- */
- if (port->type == HSR_PT_INTERLINK) {
- spin_lock_bh(&hsr->seqnr_lock);
- hsr_forward_skb(skb, port);
- spin_unlock_bh(&hsr->seqnr_lock);
- } else {
- hsr_forward_skb(skb, port);
- }
+ hsr_forward_skb(skb, port);
finish_consume:
return RX_HANDLER_CONSUMED;
@@ -170,6 +161,7 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,
if (res)
goto fail_rx_handler;
dev_disable_lro(dev);
+ dev_disable_gro(dev);
return 0;
diff --git a/tools/testing/selftests/net/hsr/Makefile b/tools/testing/selftests/net/hsr/Makefile
index 31fb9326cf533..0d105476e7c50 100644
--- a/tools/testing/selftests/net/hsr/Makefile
+++ b/tools/testing/selftests/net/hsr/Makefile
@@ -3,6 +3,7 @@
top_srcdir = ../../../../..
TEST_PROGS := \
+ hsr_gro_superpacket.sh \
hsr_ping.sh \
hsr_redbox.sh \
link_faults.sh \
diff --git a/tools/testing/selftests/net/hsr/hsr_gro_superpacket.sh b/tools/testing/selftests/net/hsr/hsr_gro_superpacket.sh
new file mode 100755
index 0000000000000..288de3a60b899
--- /dev/null
+++ b/tools/testing/selftests/net/hsr/hsr_gro_superpacket.sh
@@ -0,0 +1,462 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test HSR handling of GRO/GSO super-packets:
+#
+# 1. Enslaving a device to an HSR master disables GRO on it
+# (dev_disable_gro()).
+# 2. The HSR master does not advertise GSO/TSO features.
+# 3. A TCP stream from a TSO-enabled SAN (which therefore emits GSO
+# super-packets) is unfolded at the HSR forward entry. Evidence:
+# interface-counter deltas show super-packet-sized frames leaving
+# the SAN and per-frame-sized traffic leaving the DUT's LAN ports.
+#
+# Topology (100.64.0.0/24):
+#
+# ns_san ns_dut ns_peer
+# +-----------+ interlink +---------------+ LAN A/B +-----------+
+# | s0 [0.1] |-------------| d_il hsr0 |-----------| hsr1 [0.3]|
+# +-----------+ | d_a / d_b | | p_a / p_b |
+# +---------------+ +-----------+
+#
+# SAN traffic reaches ns_peer only through hsr0's forward path
+# (interlink RX -> LAN A/B TX), so every SAN frame is tagged and
+# forwarded by the DUT.
+
+source ./hsr_common.sh
+
+san_ip="100.64.0.1"
+peer_ip="100.64.0.3"
+
+# Aggregate counter thresholds for the stream test (bytes/packets):
+# SAN_AVG_MIN proves GSO super-packets left the SAN; LAN_AVG_MAX is a
+# guard with margin, not the protocol maximum (see do_tso_stream_test).
+SAN_AVG_MIN=2048
+LAN_AVG_MAX=1514
+
+iperf_pid=""
+server_wrapper=""
+workdir=""
+pidfile=""
+rcfile=""
+
+cleanup()
+{
+ # exact-PID kill only after RE-validating identity (guards against
+ # PID reuse between publication and cleanup)
+ if [ -n "${iperf_pid}" ] && valid_server_pid "${iperf_pid}"; then
+ kill "${iperf_pid}" 2>/dev/null
+ fi
+ iperf_pid=""
+ if [ -n "${server_wrapper}" ]; then
+ # the wrapper waits on the server; reap it with a 5s bound so a
+ # live-but-unpublished server can never hang cleanup
+ for _ in $(seq 1 50); do
+ kill -0 "${server_wrapper}" 2>/dev/null || break
+ sleep 0.1
+ done
+ kill "${server_wrapper}" 2>/dev/null
+ wait "${server_wrapper}" 2>/dev/null
+ server_wrapper=""
+ fi
+ # last resort, namespace-scoped only: TERM the iperf3 processes that
+ # actually live in the peer netns, poll for bounded exit, then
+ # SIGKILL any survivor before touching the namespace name. A blind
+ # pkill would scan the host PID space and hit unrelated tests.
+ local _p _still
+ for _p in $(ip netns pids "$ns_peer" 2>/dev/null); do
+ if is_iperf3_pid "$_p"; then
+ kill "$_p" 2>/dev/null
+ fi
+ done
+ for _ in $(seq 1 50); do
+ _still=0
+ for _p in $(ip netns pids "$ns_peer" 2>/dev/null); do
+ if is_iperf3_pid "$_p"; then
+ _still=1
+ break
+ fi
+ done
+ [ "$_still" -eq 0 ] && break
+ sleep 0.1
+ done
+ for _p in $(ip netns pids "$ns_peer" 2>/dev/null); do
+ if is_iperf3_pid "$_p"; then
+ kill -9 "$_p" 2>/dev/null
+ fi
+ done
+ # remove only the known non-empty private directory
+ if [ -n "${workdir}" ] && [ -d "${workdir}" ]; then
+ rm -rf "${workdir}"
+ fi
+ workdir=""
+ pidfile=""
+ rcfile=""
+ cleanup_all_ns
+}
+
+trap cleanup EXIT
+
+check_tool()
+{
+ if ! command -v "$1" > /dev/null 2>&1; then
+ echo "SKIP: Could not run test without $1"
+ exit $ksft_skip
+ fi
+}
+
+nsx()
+{
+ ip netns exec "$1" bash -c "$2"
+}
+
+is_iperf3_pid()
+{
+ [ "$(cat /proc/"$1"/comm 2>/dev/null)" = "iperf3" ]
+}
+
+# Decimal-counter validation for the snapshot blocks: every value must
+# be a plain decimal number. A parse failure in read_tx_counters yields
+# empty/garbled fields, which this check turns into an immediate FAIL.
+valid_decimals()
+{
+ local v
+
+ for v in "$@"; do
+ [[ "$v" =~ ^[0-9]+$ ]] || return 1
+ done
+ return 0
+}
+
+setup_topo()
+{
+ setup_ns ns_dut ns_san ns_peer || exit $?
+
+ ip link add d_a netns "$ns_dut" type veth peer name p_a netns "$ns_peer"
+ ip link add d_b netns "$ns_dut" type veth peer name p_b netns "$ns_peer"
+ ip link add d_il netns "$ns_dut" type veth peer name s0 netns "$ns_san"
+
+ # HSR tags add 6 bytes per frame; give the LAN legs headroom.
+ for iface in d_a d_b; do
+ nsx "$ns_dut" "ip link set $iface mtu 1600; \
+ ip link set $iface up"
+ done
+ for iface in p_a p_b; do
+ nsx "$ns_peer" "ip link set $iface mtu 1600; \
+ ip link set $iface up"
+ done
+
+ nsx "$ns_dut" "ip link set d_il up"
+ nsx "$ns_san" "ip link set s0 up; ip addr add $san_ip/24 dev s0"
+
+ nsx "$ns_dut" "ip link add hsr0 type hsr \
+ slave1 d_a slave2 d_b interlink d_il proto 0; \
+ ip link set hsr0 up"
+ nsx "$ns_peer" "ip link add hsr1 type hsr \
+ slave1 p_a slave2 p_b proto 0; \
+ ip link set hsr1 up; ip addr add $peer_ip/24 dev hsr1"
+
+ # Let the nodes see each other's supervision frames.
+ sleep 2
+}
+
+check_feature()
+{
+ local ns="$1"
+ local iface="$2"
+ local feature="$3"
+ local want="$4"
+
+ if nsx "$ns" "ethtool -k $iface" | grep -q "^$feature: $want"; then
+ echo "INFO: $ns/$iface $feature is $want [ OK ]"
+ else
+ echo "FAIL: $ns/$iface $feature is not $want" 1>&2
+ ret=1
+ fi
+}
+
+# Off-or-absent variant: fails only when the feature is present AND on,
+# so devices that simply do not list the feature do not fail it.
+check_feature_not_on()
+{
+ local ns="$1"
+ local iface="$2"
+ local feature="$3"
+
+ if nsx "$ns" "ethtool -k $iface" | grep -q "^$feature: on"; then
+ echo "FAIL: $ns/$iface $feature is on" 1>&2
+ ret=1
+ else
+ echo "INFO: $ns/$iface $feature not on [ OK ]"
+ fi
+}
+
+do_gro_feature_checks()
+{
+ echo "INFO: Checking that enslavement disabled GRO."
+ check_feature "$ns_dut" d_a generic-receive-offload off
+ check_feature "$ns_dut" d_b generic-receive-offload off
+ check_feature "$ns_dut" d_il generic-receive-offload off
+ stop_if_error "GRO not disabled on enslaved devices."
+
+ echo "INFO: Checking that enslavement disabled HW-GRO."
+ check_feature "$ns_dut" d_a rx-gro-hw off
+ check_feature "$ns_dut" d_b rx-gro-hw off
+ check_feature "$ns_dut" d_il rx-gro-hw off
+ stop_if_error "HW-GRO not disabled on enslaved devices."
+
+ echo "INFO: Checking that the HSR master does not advertise GSO/TSO."
+ check_feature "$ns_dut" hsr0 generic-segmentation-offload off
+ check_feature "$ns_dut" hsr0 tcp-segmentation-offload off
+ check_feature_not_on "$ns_dut" hsr0 tx-tcp6-segmentation
+ check_feature_not_on "$ns_dut" hsr0 tx-udp-segmentation
+ check_feature_not_on "$ns_dut" hsr0 tx-gso-list
+ stop_if_error "HSR master still advertises GSO-family features."
+}
+
+alloc_workdir()
+{
+ # Allocated only here, long after the initial topology cleanup, so
+ # cleanup() at setup_topo() time can never remove it. mktemp failure
+ # is a hard test failure.
+ workdir=$(mktemp -d /tmp/hsr_gro_test.XXXXXX) || {
+ echo "FAIL: mktemp -d failed" 1>&2
+ exit 1
+ }
+ chmod 700 "${workdir}"
+ pidfile="${workdir}/iperf.pid"
+ rcfile="${workdir}/iperf.rc"
+}
+
+# Numeric, alive, comm == iperf3, and really owned by the peer netns.
+valid_server_pid()
+{
+ local p="$1"
+
+ [[ "$p" =~ ^[0-9]+$ ]] || return 1
+ kill -0 "$p" 2>/dev/null || return 1
+ [ "$(cat /proc/"$p"/comm 2>/dev/null)" = "iperf3" ] || return 1
+ ip netns pids "$ns_peer" 2>/dev/null | grep -qx "$p"
+}
+
+start_iperf_server()
+{
+ local candidate_pid
+
+ # One-shot server, no -D: the wrapper records its exact PID and its
+ # real exit status (netns shares the PID namespace and the host fs).
+ alloc_workdir
+ ( nsx "$ns_peer" "iperf3 -s -1 > /dev/null 2>&1 & \
+ echo \$! > ${pidfile}; \
+ wait \$!; \
+ echo \$? > ${rcfile}" ) &
+ server_wrapper=$!
+ # the wrapper writes the pidfile asynchronously; wait for it to
+ # appear instead of racing the read
+ for _ in $(seq 1 50); do
+ [ -s "${pidfile}" ] && break
+ sleep 0.1
+ done
+ if [ ! -s "${pidfile}" ]; then
+ echo "FAIL: iperf3 server did not publish a pid" \
+ "(no pidfile)" 1>&2
+ ret=1
+ return 1
+ fi
+ candidate_pid=$(<"${pidfile}")
+ if ! valid_server_pid "${candidate_pid}"; then
+ echo "FAIL: iperf3 server pid '${candidate_pid}'" \
+ "failed validation" 1>&2
+ ret=1
+ return 1
+ fi
+ # publish only after full validation
+ iperf_pid="${candidate_pid}"
+ sleep 1
+ return 0
+}
+
+# Print "<bytes> <packets>" for exactly one TX record of ns/dev; anything
+# else (missing, duplicated, non-numeric) is a hard FAIL.
+read_tx_counters()
+{
+ local ns="$1" dev="$2"
+ local out cnt
+
+ out=$(nsx "$ns" "ip -s link show $dev" | \
+ awk '/^ +TX:/{getline; print $1, $2}')
+ cnt=$(echo "$out" | grep -c '^[0-9]* [0-9]*$')
+ if [ "$cnt" -ne 1 ]; then
+ echo "FAIL: cannot parse TX counters of $ns/$dev" \
+ "(records=$cnt)" 1>&2
+ return 1
+ fi
+ echo "$out"
+ return 0
+}
+
+eval_counter_delta()
+{
+ local name="$1" b0="$2" p0="$3" b1="$4" p1="$5" op="$6" limit="$7"
+ local bd pd
+
+ if ! [[ "$b0" =~ ^[0-9]+$ && "$b1" =~ ^[0-9]+$ && \
+ "$p0" =~ ^[0-9]+$ && "$p1" =~ ^[0-9]+$ ]]; then
+ echo "FAIL: non-numeric counter input for $name" 1>&2
+ ret=1
+ return 1
+ fi
+ bd=$((b1 - b0))
+ pd=$((p1 - p0))
+ if [ "$bd" -lt 0 ] || [ "$pd" -le 0 ]; then
+ echo "FAIL: counter delta invalid for $name" \
+ "(bytes=$bd pkts=$pd)" 1>&2
+ ret=1
+ return 1
+ fi
+ if [ "$op" = "gt" ]; then
+ if [ "$bd" -le $((pd * limit)) ]; then
+ echo "FAIL: $name bytes/packets $bd/$pd <= $limit" 1>&2
+ ret=1
+ return 1
+ fi
+ else
+ if [ "$bd" -gt $((pd * limit)) ]; then
+ echo "FAIL: $name bytes/packets $bd/$pd > $limit" 1>&2
+ ret=1
+ return 1
+ fi
+ fi
+ echo "INFO: $name counter delta bytes=$bd packets=$pd" \
+ "(op $op limit $limit) [ OK ]"
+ return 0
+}
+
+do_tso_stream_test()
+{
+ local out sender_retr server_rc
+ local san_b0 san_p0 san_b1 san_p1
+ local a_b0 a_p0 a_b1 a_p1 b_b0 b_p0 b_b1 b_p1
+
+ echo "INFO: Enabling TSO/GSO on the SAN interface."
+ nsx "$ns_san" "ethtool -K s0 tso on gso on"
+ check_feature "$ns_san" s0 tcp-segmentation-offload on
+ stop_if_error "Could not enable TSO on the SAN interface."
+
+ echo "INFO: Running 10s TCP stream SAN -> peer through the HSR DUT."
+ start_iperf_server || return
+
+ # Counter snapshots around the stream window. The SAN-side average
+ # must exceed SAN_AVG_MIN (aggregate proof that GSO super-packets
+ # really left the SAN); each DUT LAN leg must stay under LAN_AVG_MAX
+ # (aggregate proof that bulk output was segmented per-frame). These
+ # are aggregate discriminators, not a per-frame maximum proof.
+ san_b0=0; san_p0=0; a_b0=0; a_p0=0; b_b0=0; b_p0=0
+ read -r san_b0 san_p0 <<EOF
+$(read_tx_counters "$ns_san" s0)
+EOF
+ read -r a_b0 a_p0 <<EOF
+$(read_tx_counters "$ns_dut" d_a)
+EOF
+ read -r b_b0 b_p0 <<EOF
+$(read_tx_counters "$ns_dut" d_b)
+EOF
+ if ! valid_decimals "$san_b0" "$san_p0" "$a_b0" "$a_p0" \
+ "$b_b0" "$b_p0"; then
+ echo "FAIL: baseline TX counter snapshot invalid" 1>&2
+ ret=1
+ return 1
+ fi
+
+ # rate-capped: the PRIMARY discriminator is the counter inequality
+ # above, not max throughput; retransmits are informational only.
+ # Uncapped runs flap at VM/CI edge rates without indicating a
+ # functional problem.
+ if ! out=$(nsx "$ns_san" "timeout 60 iperf3 -c $peer_ip -M 1446 \
+ -b 2G -t 10" 2>&1); then
+ echo "FAIL: iperf3 client failed:" 1>&2
+ echo "$out" 1>&2
+ ret=1
+ return
+ fi
+
+ read -r san_b1 san_p1 <<EOF
+$(read_tx_counters "$ns_san" s0)
+EOF
+ read -r a_b1 a_p1 <<EOF
+$(read_tx_counters "$ns_dut" d_a)
+EOF
+ read -r b_b1 b_p1 <<EOF
+$(read_tx_counters "$ns_dut" d_b)
+EOF
+ if ! valid_decimals "$san_b1" "$san_p1" "$a_b1" "$a_p1" \
+ "$b_b1" "$b_p1"; then
+ echo "FAIL: final TX counter snapshot invalid" 1>&2
+ ret=1
+ return 1
+ fi
+
+ eval_counter_delta "SAN s0 TX" "$san_b0" "$san_p0" "$san_b1" "$san_p1" \
+ gt "$SAN_AVG_MIN"
+ eval_counter_delta "DUT d_a TX" "$a_b0" "$a_p0" "$a_b1" "$a_p1" \
+ le "$LAN_AVG_MAX"
+ eval_counter_delta "DUT d_b TX" "$b_b0" "$b_p0" "$b_b1" "$b_p1" \
+ le "$LAN_AVG_MAX"
+ [ "${ret:-0}" -eq 0 ] || return
+
+ # success path: the one-shot server exits by itself; reap the
+ # wrapper, then REQUIRE the rcfile with the server's real status
+ wait "${server_wrapper}"
+ server_wrapper=""
+ if [ ! -s "${rcfile}" ]; then
+ echo "FAIL: iperf3 server status file missing (${rcfile})" 1>&2
+ ret=1
+ return
+ fi
+ server_rc=$(cat "${rcfile}")
+ if ! [[ "$server_rc" =~ ^[0-9]+$ ]] || [ "$server_rc" -ne 0 ]; then
+ echo "FAIL: iperf3 server exited with rc='${server_rc}'" 1>&2
+ ret=1
+ return
+ fi
+ iperf_pid=""
+
+ # secondary health signal only: anchored, single-match, numeric —
+ # any parse anomaly is a loud FAIL, but the value itself no longer
+ # gates (the counter inequalities above are the primary evidence).
+ sender_retr=$(echo "$out" | awk '/sec .* sender$/ {print $(NF-1)}')
+ if [ "$(echo "$sender_retr" | grep -Ec '^[0-9]+$')" -ne 1 ]; then
+ echo "FAIL: cannot parse sender retransmits reliably" 1>&2
+ echo "$out" 1>&2
+ ret=1
+ return
+ fi
+ echo "INFO: TCP stream done;" \
+ "sender retransmits=$sender_retr (secondary signal)"
+ echo "$out" | grep -E "sender|receiver"
+}
+
+check_prerequisites
+check_tool ethtool
+check_tool iperf3
+check_tool timeout
+
+# iproute2 must know the HSR interlink syntax.
+if ! ip link help hsr 2>&1 | grep -qi interlink; then
+ echo "SKIP: iproute2 has no HSR interlink support"
+ exit $ksft_skip
+fi
+
+setup_topo
+
+echo "INFO: Initial validation ping (SAN -> peer through the DUT)."
+do_ping "$ns_san" "$peer_ip"
+stop_if_error "Initial validation failed."
+
+do_gro_feature_checks
+do_tso_stream_test
+stop_if_error "GSO super-packet stream test failed."
+
+echo "INFO: All good."
+cleanup
+exit $ret
|
| 3/2 | 2026/08/01 15:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11160 output: 25 thoughts: 227 Thoughts: **Analyzing the GRO Disabling Patch** Okay, so I'm looking at this patch. It introduces `netif_disable_gro` and `dev_disable_gro` in the core networking files, specifically `net/core/dev.c` and `net/core/dev_api.c`. That's a pretty low-level change, impacting fundamental network device behavior. Then, it touches HSR - High-availability Seamless Redundancy - code: `net/hsr/hsr_device.c`, `net/hsr/hsr_forward.c`, and `net/hsr/hsr_slave.c`. Looks like it's adding support for GSO super-packets in the HSR context. And there's a selftest in `tools/testing/selftests/net/hsr/hsr_gro_superpacket.sh`, which is good to see; a test suite is crucial here. From a quick glance, this is a pretty interesting functional patch. Modifying both core networking and specific driver code, particularly one dealing with redundancy and potentially complex forwarding logic makes me think this should be fuzzed thoroughly. The changes to `dev_disable_gro` in core and `net/hsr/*` indicate it may interact with other networking functions. Now, let's figure out what CONFIG options are involved. Since the HSR code is in `net/hsr/`, I should start by checking `net/hsr/Kconfig` or possibly even `net/Kconfig` to see what actually enables the HSR functionality. Knowing the dependencies will help us target our fuzzing and testing efforts more effectively. |
| 4/2 | 2026/08/01 15:46 | tool | grepper |
0mArgs:{
"Expression": "config HSR",
"PathPrefix": "net/"
}Results: {
"Output": "net/hsr/Kconfig-5-\nnet/hsr/Kconfig:6:config HSR\nnet/hsr/Kconfig-7-\ttristate \"High-availability Seamless Redundancy (HSR \u0026 PRP)\"\n"
} |
| 5/2 | 2026/08/01 15:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11833 output: 33 thoughts: 23 Thoughts: Here's my take on those thoughts, tailored for an expert audience: **Diving into the CONFIG_HSR Dependencies** Okay, so `CONFIG_HSR` is the big one. That's where we start, of course. I need to understand its place within the broader configuration landscape. Before I even think about enabling or disabling it, or tweaking related settings, I've got to ensure I have a solid grasp on what other components, features, or architectural considerations it might hinge upon. Are there any prerequisites? Does it require specific hardware support, kernel modules, or other config options to be active? Or potentially conflicts? I'm going to quickly check for any immediate dependencies, both explicit and implicit, to form the best and complete mental model. Gotta make sure it will not introduce conflicts, before changing the configuration! |
| 6/2 | 2026/08/01 15:46 | tool | read-file |
0mArgs:{
"File": "net/hsr/Kconfig",
"FirstLine": 1,
"LineCount": 20
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\t#\n 3:\t# IEC 62439-3 High-availability Seamless Redundancy\n 4:\t#\n 5:\t\n 6:\tconfig HSR\n 7:\t\ttristate \"High-availability Seamless Redundancy (HSR \u0026 PRP)\"\n 8:\t\thelp\n 9:\t\t This enables IEC 62439 defined High-availability Seamless\n 10:\t\t Redundancy (HSR) and Parallel Redundancy Protocol (PRP).\n 11:\t\n 12:\t\t If you say Y here, then your Linux box will be able to act as a\n 13:\t\t DANH (\"Doubly attached node implementing HSR\") or DANP (\"Doubly\n 14:\t\t attached node implementing PRP\"). For this to work, your Linux box\n 15:\t\t needs (at least) two physical Ethernet interfaces.\n 16:\t\n 17:\t\t For DANH, it must be connected as a node in a ring network together\n 18:\t\t with other HSR capable nodes. All Ethernet frames sent over the HSR\n 19:\t\t device will be sent in both directions on the ring (over both slave\n 20:\t\t ports), giving a redundant, instant fail-over network. Each HSR node\n"
} |
| 7/2 | 2026/08/01 15:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12399 output: 32 thoughts: 105 Thoughts: **Analyzing the GRO Disabling Patch** Okay, so this patch doesn't appear to be introducing anything particularly exotic in terms of dependencies – just standard networking components. My initial thought is to zero in on the core of the implementation. I'll need to verify the specific functions impacted. I should start by examining `net/core/dev.c` and `net/core/dev_api.c`. My aim is to locate `dev_disable_gro` and understand how it's being integrated. Ah, yes, it looks like `dev_disable_gro` is indeed added and exposed. Now I need to understand the implications of it being used and how it interacts with existing logic. The patch also touches `net/hsr/hsr_forward.c`, specifically the `hsr_forward_skb` function. This is significant. I'll have to delve into that function to see precisely how GRO disablement is being invoked and why, particularly in relation to HSR (High-availability Seamless Redundancy) forwarding. I need to understand the rationale for disabling GRO within this specific context. What impact does that have on performance and reliability? I'll need to dig into `hsr_forward_skb` thoroughly. |
| 8/2 | 2026/08/01 15:46 | tool | grepper |
0mArgs:{
"Expression": "hsr_forward_skb",
"PathPrefix": "net/hsr/"
}Results: {
"Output": "net/hsr/hsr_device.c=224=static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)\n--\nnet/hsr/hsr_device.c-234-\t\tskb_reset_mac_len(skb);\nnet/hsr/hsr_device.c:235:\t\thsr_forward_skb(skb, master);\nnet/hsr/hsr_device.c-236-\t} else {\n--\nnet/hsr/hsr_device.c=291=static void send_hsr_supervision_frame(struct hsr_port *port,\n--\nnet/hsr/hsr_device.c-360-\nnet/hsr/hsr_device.c:361:\thsr_forward_skb(skb, port);\nnet/hsr/hsr_device.c-362-}\n--\nnet/hsr/hsr_device.c=364=static void send_prp_supervision_frame(struct hsr_port *master,\n--\nnet/hsr/hsr_device.c-398-\nnet/hsr/hsr_device.c:399:\thsr_forward_skb(skb, master);\nnet/hsr/hsr_device.c-400-}\n--\nnet/hsr/hsr_forward.c=678=static int fill_frame_info(struct hsr_frame_info *frame,\n--\nnet/hsr/hsr_forward.c-735-/* Must be called holding rcu read lock (because of the port parameter) */\nnet/hsr/hsr_forward.c:736:static void hsr_forward_skb_one(struct sk_buff *skb, struct hsr_port *port)\nnet/hsr/hsr_forward.c-737-{\n--\nnet/hsr/hsr_forward.c-768- */\nnet/hsr/hsr_forward.c:769:void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)\nnet/hsr/hsr_forward.c-770-{\n--\nnet/hsr/hsr_forward.c-773-\tif (likely(!skb_is_gso(skb))) {\nnet/hsr/hsr_forward.c:774:\t\thsr_forward_skb_one(skb, port);\nnet/hsr/hsr_forward.c-775-\t\treturn;\n--\nnet/hsr/hsr_forward.c-802-\t\tsegs-\u003enext = NULL;\nnet/hsr/hsr_forward.c:803:\t\thsr_forward_skb_one(segs, port);\nnet/hsr/hsr_forward.c-804-\t\tsegs = next;\n--\nnet/hsr/hsr_forward.h-15-\nnet/hsr/hsr_forward.h:16:void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port);\nnet/hsr/hsr_forward.h-17-struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,\n--\nnet/hsr/hsr_slave.c=24=static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)\n--\nnet/hsr/hsr_slave.c-75-\nnet/hsr/hsr_slave.c:76:\thsr_forward_skb(skb, port);\nnet/hsr/hsr_slave.c-77-\n"
} |
| 9/2 | 2026/08/01 15:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13743 output: 32 thoughts: 123 Thoughts: **Analyzing the Network Traffic Patch** Okay, so this patch introduces a couple of key functionalities. First, it's adding `hsr_forward_skb`, which I immediately recognize as related to Generic Segmentation Offload (GSO) and, specifically, the process of handling those large "super-packets." It sounds like we're unfolding these, likely to deal with them appropriately within the High-availability Seamless Redundancy (HSR) context. Then we've got `dev_disable_gro` and `netif_disable_gro`. These are clearly about disabling GRO at different levels – device and network interface, respectively. This triggers a question; why are we disabling GRO? Is it due to potential interactions or conflicts within the HSR implementation, or perhaps to ensure specific packet handling behaviour? I'll need to dig deeper there. To get a better grasp of the overall flow, let me quickly refocus on the provided key symbols: `hsr_forward_skb`, as mentioned. And then, it mentions the disabling functions, `dev_disable_gro` and `netif_disable_gro`. Given the context, I want to make sure I understand the `hsr_dev_xmit` and `hsr_handle_frame` points. They're likely the entry points for the HSR-specific packet transmission and reception, so I'll need to verify that those are the correct and expected places where this logic integrates. I'll take a look at the surrounding code for each of those. |
| 10/2 | 2026/08/01 15:46 | tool | grepper |
0mArgs:{
"Expression": "hsr_dev_xmit",
"PathPrefix": "net/hsr/"
}Results: {
"Output": "net/hsr/hsr_device.c=216=static netdev_features_t hsr_fix_features(struct net_device *dev,\n--\nnet/hsr/hsr_device.c-223-\nnet/hsr/hsr_device.c:224:static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)\nnet/hsr/hsr_device.c-225-{\n--\nnet/hsr/hsr_device.c=598=static const struct net_device_ops hsr_device_ops = {\n--\nnet/hsr/hsr_device.c-601-\t.ndo_stop = hsr_dev_close,\nnet/hsr/hsr_device.c:602:\t.ndo_start_xmit = hsr_dev_xmit,\nnet/hsr/hsr_device.c-603-\t.ndo_change_rx_flags = hsr_change_rx_flags,\n--\nnet/hsr/hsr_device.c=664=bool is_hsr_master(struct net_device *dev)\nnet/hsr/hsr_device.c-665-{\nnet/hsr/hsr_device.c:666:\treturn (dev-\u003enetdev_ops-\u003endo_start_xmit == hsr_dev_xmit);\nnet/hsr/hsr_device.c-667-}\n"
} |
| 11/2 | 2026/08/01 15:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14599 output: 31 thoughts: 37 Thoughts: Here's my understanding of the situation, tailored for an expert audience: **HSR Device Transmission and Reception** Alright, so let's break this down. From what I'm seeing, `hsr_dev_xmit` is essentially the HSR-specific implementation of `ndo_start_xmit`. In other words, this function handles the actual transmission of frames for HSR (High-availability Seamless Redundancy) devices. So, when a frame needs to go out onto the HSR network, this is the function that's getting called. This is the starting point for transmission for HSR devices. On the other side, for receiving, `hsr_handle_frame` is the registered handler for the RX process. This function is the primary entry point for processing incoming HSR frames. Got it. Now, the key is how these functions work together to achieve the seamless redundancy that HSR is known for, and the details of that are critical to analyze. |
| 12/2 | 2026/08/01 15:46 | tool | grepper |
0mArgs:{
"Expression": "hsr_handle_frame",
"PathPrefix": "net/hsr/"
}Results: {
"Output": "net/hsr/hsr_device.c=713=int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],\n--\nnet/hsr/hsr_device.c-739-\nnet/hsr/hsr_device.c:740:\t/* Make sure we recognize frames from ourselves in hsr_handle_frame() */\nnet/hsr/hsr_device.c-741-\tres = hsr_create_self_node(hsr, hsr_dev-\u003edev_addr,\n--\nnet/hsr/hsr_framereg.c=66=bool hsr_is_node_in_db(struct list_head *node_db,\n--\nnet/hsr/hsr_framereg.c-71-\nnet/hsr/hsr_framereg.c:72:/* Helper for device init; the self_node is used in hsr_handle_frame() to\nnet/hsr/hsr_framereg.c-73- * recognize frames from self that's been looped over the HSR ring.\n--\nnet/hsr/hsr_main.c=31=static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,\n--\nnet/hsr/hsr_main.c-91-\nnet/hsr/hsr_main.c:92:\t\t/* Make sure we recognize frames from ourselves in hsr_handle_frame() */\nnet/hsr/hsr_main.c-93-\t\tport = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);\n--\nnet/hsr/hsr_slave.c=19=bool hsr_invalid_dan_ingress_frame(__be16 protocol)\n--\nnet/hsr/hsr_slave.c-23-\nnet/hsr/hsr_slave.c:24:static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)\nnet/hsr/hsr_slave.c-25-{\n--\nnet/hsr/hsr_slave.c=85=bool hsr_port_exists(const struct net_device *dev)\nnet/hsr/hsr_slave.c-86-{\nnet/hsr/hsr_slave.c:87:\treturn rcu_access_pointer(dev-\u003erx_handler) == hsr_handle_frame;\nnet/hsr/hsr_slave.c-88-}\n--\nnet/hsr/hsr_slave.c=132=static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,\n--\nnet/hsr/hsr_slave.c-159-\nnet/hsr/hsr_slave.c:160:\tres = netdev_rx_handler_register(dev, hsr_handle_frame, port);\nnet/hsr/hsr_slave.c-161-\tif (res)\n"
} |
| 13/2 | 2026/08/01 15:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15626 output: 172 thoughts: 330 Thoughts: **My Analysis of the HSR Patch** Okay, let's break this down. My understanding is that the critical function here is `hsr_forward_skb` because that's the one that's been modified. Also, the Rx handler, `hsr_handle_frame`, is vital to the HSR processing flow. They've also introduced `dev_disable_gro` and `netif_disable_gro` which are interesting additions. The `HSR` config is the key to enabling this functionality, which makes complete sense. We have to double check if any other configs are needed, but since `net/core/dev.c` is always compiled and `net/hsr/` is compiled when `CONFIG_HSR` is enabled, then we can confidently state that `EnableConfigs` is just `["HSR"]`. As for which functions to focus on for fuzzing? Well, besides `hsr_forward_skb`, we should definitely include `dev_disable_gro` and `netif_disable_gro` as these are integral to the GRO disabling aspect of the changes and would be important fuzzing targets. Based on my knowledge, `hsr_dev_xmit` should be included since it handles transmission, and `hsr_handle_frame` should also be included as it is the rx handler. Thus, `FocusSymbols` is defined as `["hsr_forward_skb", "dev_disable_gro", "netif_disable_gro", "hsr_dev_xmit", "hsr_handle_frame"]`. Now, the question is whether `dev_disable_gro` and `netif_disable_gro` are exported? Yes, after a quick check they're exported in `net/core/dev_api.c` and `net/core/dev.c`, respectively, so they are accessible. The reasoning here is solid. The patch adds functions to disable GRO and modifies the HSR driver to unfold GSO super-packets in `hsr_forward_skb`. It also disables GRO on HSR slave devices. These are fundamental changes to networking core and the HSR driver, and any traffic through an HSR interface is going to touch these modifications. That makes them excellent candidates for fuzzing, given the potential for exploiting unexpected behavior during packet processing, and is a pretty obvious conclusion. Right, I'm ready to call `set-results` now. |
| 14/2 | 2026/08/01 15:46 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"HSR"
],
"FocusSymbols": [
"hsr_forward_skb",
"dev_disable_gro",
"netif_disable_gro",
"hsr_dev_xmit",
"hsr_handle_frame"
],
"Reasoning": "The patch adds `dev_disable_gro` and `netif_disable_gro` to disable GRO on net devices, and modifies the HSR driver to unfold GSO super-packets in `hsr_forward_skb`. It also disables GRO on HSR slave devices. These are functional changes to core networking and the HSR driver that can be reached by sending traffic through an HSR interface, making them highly relevant for fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"HSR"
],
"FocusSymbols": [
"hsr_forward_skb",
"dev_disable_gro",
"netif_disable_gro",
"hsr_dev_xmit",
"hsr_handle_frame"
],
"Reasoning": "The patch adds `dev_disable_gro` and `netif_disable_gro` to disable GRO on net devices, and modifies the HSR driver to unfold GSO super-packets in `hsr_forward_skb`. It also disables GRO on HSR slave devices. These are functional changes to core networking and the HSR driver that can be reached by sending traffic through an HSR interface, making them highly relevant for fuzzing.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|