| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/27 07:29 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"NET_SCH_FQ",
"NET_PKTGEN",
"ETHTOOL_NETLINK"
],
"FocusSymbols": [
"fq_change",
"fq_dequeue",
"fq_classify",
"ethnl_set_rings",
"pktgen_if_write",
"pktgen_xmit"
],
"Reasoning": "The patch introduces a new feature for pacing offload horizon. It modifies the core Fair Queue packet scheduler (sch_fq.c), the packet generator (pktgen.c), and the ethtool netlink interface (rings.c) to support configuring and using this horizon. These core networking components are reachable and functional in virtualized environments, making them suitable for fuzzing. The changes to the idpf driver are hardware-specific and not emulated, but the core changes are sufficient to warrant fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/27 07:29 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 16d32b739584559558e964a2d703b79f2248b4ba\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Jul 27 07:29:29 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml\nindex 5dd4d1b5d94bbb..c780684ca1bc0c 100644\n--- a/Documentation/netlink/specs/ethtool.yaml\n+++ b/Documentation/netlink/specs/ethtool.yaml\n@@ -448,6 +448,12 @@ attribute-sets:\n -\n name: hds-thresh-max\n type: u32\n+ -\n+ name: pacing-offload-horizon\n+ type: u32\n+ -\n+ name: pacing-offload-horizon-max\n+ type: u32\n \n -\n name: mm-stat\n@@ -2168,6 +2174,8 @@ operations:\n - tx-push-buf-len-max\n - hds-thresh\n - hds-thresh-max\n+ - pacing-offload-horizon\n+ - pacing-offload-horizon-max\n dump: *ring-get-op\n -\n name: rings-set\ndiff --git a/Documentation/networking/net_cachelines/net_device.rst b/Documentation/networking/net_cachelines/net_device.rst\nindex 512f6d6fa3d8f8..3b9ce1c6e10578 100644\n--- a/Documentation/networking/net_cachelines/net_device.rst\n+++ b/Documentation/networking/net_cachelines/net_device.rst\n@@ -183,7 +183,8 @@ struct devlink_port* devlink_port\n struct dpll_pin* dpll_pin\n struct hlist_head page_pools\n struct dim_irq_moder* irq_moder\n-u64 max_pacing_offload_horizon\n+u32 pacing_offload_horizon\n+u32 max_pacing_offload_horizon\n struct_napi_config* napi_config\n unsigned_long gro_flush_timeout\n u32 napi_defer_hard_irqs\ndiff --git a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c\nindex bb99d9e7c65d51..5ddf347927dc3e 100644\n--- a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c\n+++ b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c\n@@ -1801,7 +1801,8 @@ static void idpf_get_ts_stats(struct net_device *netdev,\n static const struct ethtool_ops idpf_ethtool_ops = {\n \t.supported_coalesce_params = ETHTOOL_COALESCE_USECS |\n \t\t\t\t ETHTOOL_COALESCE_USE_ADAPTIVE,\n-\t.supported_ring_params\t= ETHTOOL_RING_USE_TCP_DATA_SPLIT,\n+\t.supported_ring_params\t= ETHTOOL_RING_USE_TCP_DATA_SPLIT |\n+\t\t\t\t ETHTOOL_RING_USE_PACING_OFFLOAD_HORIZON,\n \t.get_msglevel\t\t= idpf_get_msglevel,\n \t.set_msglevel\t\t= idpf_set_msglevel,\n \t.get_link\t\t= ethtool_op_get_link,\ndiff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c\nindex cf966fe6c759ce..de4170c2aa3ba0 100644\n--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c\n+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c\n@@ -888,6 +888,11 @@ static int idpf_cfg_netdev(struct idpf_vport *vport)\n \tnetdev-\u003emin_mtu = ETH_MIN_MTU;\n \tnetdev-\u003emax_mtu = vport-\u003emax_mtu;\n \n+\tif (idpf_is_queue_model_split(vport-\u003edflt_qv_rsrc.txq_model) \u0026\u0026\n+\t !idpf_is_cap_ena(adapter, IDPF_OTHER_CAPS,\n+\t\t\t VIRTCHNL2_CAP_SPLITQ_QSCHED))\n+\t\tnetdev-\u003emax_pacing_offload_horizon = 128000;\n+\n \tdflt_features = NETIF_F_SG\t|\n \t\t\tNETIF_F_HIGHDMA;\n \ndiff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c\nindex 566b08ca3a6cb1..8f11de57c8bc77 100644\n--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c\n+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c\n@@ -1,6 +1,8 @@\n // SPDX-License-Identifier: GPL-2.0-only\n /* Copyright (C) 2023 Intel Corporation */\n \n+#include \u003clinux/netpoll.h\u003e\n+\n #include \"idpf.h\"\n #include \"idpf_ptp.h\"\n #include \"idpf_virtchnl.h\"\n@@ -2408,9 +2410,13 @@ void idpf_tx_splitq_build_flow_desc(union idpf_tx_flex_desc *desc,\n \t\t\t\t struct idpf_tx_splitq_params *params,\n \t\t\t\t u16 td_cmd, u16 size)\n {\n-\t*(u32 *)\u0026desc-\u003eflow.qw1.cmd_dtype = (u8)(params-\u003edtype | td_cmd);\n+\t*(__le32 *)\u0026desc-\u003eflow.qw1.cmd_dtype = cpu_to_le32((u8)(params-\u003edtype | td_cmd));\n \tdesc-\u003eflow.qw1.rxr_bufsize = cpu_to_le16((u16)size);\n \tdesc-\u003eflow.qw1.compl_tag = cpu_to_le16(params-\u003ecompl_tag);\n+\n+\tdesc-\u003eflow.qw1.ts[0] = params-\u003eoffload.desc_ts[0];\n+\tdesc-\u003eflow.qw1.ts[1] = params-\u003eoffload.desc_ts[1];\n+\tdesc-\u003eflow.qw1.ts[2] = params-\u003eoffload.desc_ts[2];\n }\n \n /**\n@@ -3011,6 +3017,63 @@ static bool idpf_tx_splitq_need_re(struct idpf_tx_queue *tx_q)\n \treturn gap \u003e= IDPF_TX_SPLITQ_RE_MIN_GAP;\n }\n \n+static void idpf_tx_splitq_set_txtime(const struct idpf_tx_queue *tx_q,\n+\t\t\t\t const struct sk_buff *skb,\n+\t\t\t\t struct idpf_tx_splitq_params *tx_params)\n+{\n+\tconst int ts_gran_pow2 = 9;\n+\tu64 ts, now, horizon;\n+\n+\thorizon = READ_ONCE(skb-\u003edev-\u003epacing_offload_horizon);\n+\tif (!horizon)\n+\t\treturn;\n+\n+\t/* Skip if netpoll: not needed and not safe to call ktime helpers */\n+\tif (netpoll_tx_running(skb-\u003edev))\n+\t\treturn;\n+\n+\tswitch (skb-\u003etstamp_type) {\n+\tcase SKB_CLOCK_REALTIME:\n+\t\tts = ktime_to_ns(ktime_add(skb-\u003etstamp,\n+\t\t\t\t\t ktime_mono_to_any(0, TK_OFFS_TAI) -\n+\t\t\t\t\t ktime_mono_to_any(0, TK_OFFS_REAL)));\n+\t\tbreak;\n+\tcase SKB_CLOCK_MONOTONIC:\n+\t\tts = ktime_to_ns(ktime_mono_to_any(skb-\u003etstamp, TK_OFFS_TAI));\n+\t\tbreak;\n+\tcase SKB_CLOCK_TAI:\n+\t\tts = ktime_to_ns(skb-\u003etstamp);\n+\t\tbreak;\n+\tdefault:\n+\t\tWARN_ON_ONCE(1);\n+\t\treturn;\n+\t}\n+\n+\tnow = ktime_get_clocktai_ns();\n+\tif (ts \u003c now)\n+\t\treturn;\n+\n+\t/* beyond offload horizon? set overflow bit only */\n+\tif (ts \u003e now + (horizon * NSEC_PER_USEC)) {\n+\t\ttx_params-\u003eoffload.desc_ts[2] = 1 \u003c\u003c 7;\n+\t\treturn;\n+\t}\n+\n+\tts \u003e\u003e= ts_gran_pow2;\n+\n+\t/* 0 is valid 24b timestamp, but also means field unset.\n+\t * Add 512 ns (ts_gran_pow2) to avoid this case\n+\t */\n+\tif ((ts \u0026 0x7fffff) == 0) {\n+\t\ttx_params-\u003eoffload.desc_ts[0] = 1;\n+\t\treturn;\n+\t}\n+\n+\ttx_params-\u003eoffload.desc_ts[0] = ts \u0026 0xff;\n+\ttx_params-\u003eoffload.desc_ts[1] = (ts \u003e\u003e 8) \u0026 0xff;\n+\ttx_params-\u003eoffload.desc_ts[2] = ((ts \u003e\u003e 16) \u0026 0x7f);\n+}\n+\n /**\n * idpf_tx_splitq_frame - Sends buffer on Tx ring using flex descriptors\n * @skb: send buffer\n@@ -3097,6 +3160,10 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb,\n \n \t\ttx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE;\n \t\ttx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP;\n+\n+\t\tif (skb-\u003etstamp)\n+\t\t\tidpf_tx_splitq_set_txtime(tx_q, skb, \u0026tx_params);\n+\n \t\t/* Set the RE bit to periodically \"clean\" the descriptor ring.\n \t\t * MIN_GAP is set to MIN_RING size to ensure it will be set at\n \t\t * least once each time around the ring.\ndiff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h\nindex 4be5b3b6d3ed2b..865408b57b0d54 100644\n--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h\n+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h\n@@ -161,6 +161,7 @@ union idpf_tx_flex_desc {\n * @tso_segs: Number of segments to be sent\n * @tso_hdr_len: Length of headers to be duplicated\n * @td_cmd: Command field to be inserted into descriptor\n+ * @desc_ts: Flow scheduling offload timestamp\n */\n struct idpf_tx_offload_params {\n \tu32 tx_flags;\n@@ -174,6 +175,7 @@ struct idpf_tx_offload_params {\n \tu16 tso_hdr_len;\n \n \tu16 td_cmd;\n+\tu8 desc_ts[3];\n };\n \n /**\ndiff --git a/include/linux/ethtool.h b/include/linux/ethtool.h\nindex 5d491a98265ef9..adeebbf53dadea 100644\n--- a/include/linux/ethtool.h\n+++ b/include/linux/ethtool.h\n@@ -85,6 +85,8 @@ enum {\n * @tx_push_buf_max_len: Maximum allowed size of TX push buffer\n * @hds_thresh: Packet size threshold for header data split (HDS)\n * @hds_thresh_max: Maximum supported setting for @hds_threshold\n+ * @pacing_offload_horizon: pacing offload horizon value in usec\n+ * @max_pacing_offload_horizon: max pacing offload horizon value in usec\n *\n */\n struct kernel_ethtool_ringparam {\n@@ -97,6 +99,8 @@ struct kernel_ethtool_ringparam {\n \tu32\ttx_push_buf_max_len;\n \tu32\thds_thresh;\n \tu32\thds_thresh_max;\n+\tu32\tpacing_offload_horizon;\n+\tu32\tmax_pacing_offload_horizon;\n };\n \n /**\n@@ -108,6 +112,7 @@ struct kernel_ethtool_ringparam {\n * @ETHTOOL_RING_USE_TX_PUSH_BUF_LEN: capture for setting tx_push_buf_len\n * @ETHTOOL_RING_USE_TCP_DATA_SPLIT: capture for setting tcp_data_split\n * @ETHTOOL_RING_USE_HDS_THRS: capture for setting header-data-split-thresh\n+ * @ETHTOOL_RING_USE_PACING_OFFLOAD_HORIZON: capture for setting pacing offload horizon\n */\n enum ethtool_supported_ring_param {\n \tETHTOOL_RING_USE_RX_BUF_LEN\t\t= BIT(0),\n@@ -117,6 +122,7 @@ enum ethtool_supported_ring_param {\n \tETHTOOL_RING_USE_TX_PUSH_BUF_LEN\t= BIT(4),\n \tETHTOOL_RING_USE_TCP_DATA_SPLIT\t\t= BIT(5),\n \tETHTOOL_RING_USE_HDS_THRS\t\t= BIT(6),\n+\tETHTOOL_RING_USE_PACING_OFFLOAD_HORIZON\t= BIT(7),\n };\n \n #define __ETH_RSS_HASH_BIT(bit)\t((u32)1 \u003c\u003c (bit))\ndiff --git a/include/linux/netdevice.h b/include/linux/netdevice.h\nindex 8db25b79573e3c..3c2ed45b55dcad 100644\n--- a/include/linux/netdevice.h\n+++ b/include/linux/netdevice.h\n@@ -2129,7 +2129,8 @@ enum netdev_reg_state {\n *\t@dpll_pin: Pointer to the SyncE source pin of a DPLL subsystem,\n *\t\t where the clock is recovered.\n *\n- *\t@max_pacing_offload_horizon: max EDT offload horizon in nsec.\n+ *\t@pacing_offload_horizon: active pacing offload horizon in usec.\n+ *\t@max_pacing_offload_horizon: max pacing offload horizon in usec.\n *\t@napi_config: An array of napi_config structures containing per-NAPI\n *\t\t settings.\n *\t@num_napi_configs:\tnumber of allocated NAPI config structs,\n@@ -2552,7 +2553,8 @@ struct net_device {\n \t/** @irq_moder: dim parameters used if IS_ENABLED(CONFIG_DIMLIB). */\n \tstruct dim_irq_moder\t*irq_moder;\n \n-\tu64\t\t\tmax_pacing_offload_horizon;\n+\tu32\t\t\tpacing_offload_horizon;\n+\tu32\t\t\tmax_pacing_offload_horizon;\n \tstruct napi_config\t*napi_config;\n \tu32\t\t\tnum_napi_configs;\n \tu32\t\t\tnapi_defer_hard_irqs;\ndiff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h\nindex 8134baf7860f3c..6f1a2fa36157d3 100644\n--- a/include/uapi/linux/ethtool_netlink_generated.h\n+++ b/include/uapi/linux/ethtool_netlink_generated.h\n@@ -193,6 +193,8 @@ enum {\n \tETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX,\n \tETHTOOL_A_RINGS_HDS_THRESH,\n \tETHTOOL_A_RINGS_HDS_THRESH_MAX,\n+\tETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON,\n+\tETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON_MAX,\n \n \t__ETHTOOL_A_RINGS_CNT,\n \tETHTOOL_A_RINGS_MAX = (__ETHTOOL_A_RINGS_CNT - 1)\ndiff --git a/net/core/pktgen.c b/net/core/pktgen.c\nindex 5b4dd04d612476..7897e422051300 100644\n--- a/net/core/pktgen.c\n+++ b/net/core/pktgen.c\n@@ -201,6 +201,7 @@\n \tpf(SVID_RND)\t\t/* Random SVLAN ID */\t\t\t\\\n \tpf(NODE)\t\t/* Node memory alloc*/\t\t\t\\\n \tpf(SHARED)\t\t/* Shared SKB */\t\t\t\\\n+\tpf(TXTIME)\t\t/* SO_TXTIME support */\t\t\t\\\n \n #define pf(flag)\t\tflag##_SHIFT,\n enum pkt_flags {\n@@ -291,6 +292,10 @@ struct pktgen_dev {\n \tstruct page *page;\n \tu64 delay;\t\t/* nano-seconds */\n \n+\t/* TXTIME support */\n+\tu64 txtime_delay;\t/* transmit time delay in ns */\n+\tclockid_t txtime_clockid; /* clockid for SO_TXTIME */\n+\n \t__u64 count;\t\t/* Default No packets to send */\n \t__u64 sofar;\t\t/* How many pkts we've sent so far */\n \t__u64 tx_bytes;\t\t/* How many bytes we've transmitted */\n@@ -666,6 +671,17 @@ static int pktgen_if_show(struct seq_file *seq, void *v)\n \tif (pkt_dev-\u003enode \u003e= 0)\n \t\tseq_printf(seq, \" node: %d\\n\", pkt_dev-\u003enode);\n \n+\tif (pkt_dev-\u003eflags \u0026 F_TXTIME) {\n+\t\tclockid_t clockid = READ_ONCE(pkt_dev-\u003etxtime_clockid);\n+\n+\t\tseq_printf(seq, \" txtime_delay: %llu\\n\",\n+\t\t\t (unsigned long long)pkt_dev-\u003etxtime_delay);\n+\t\tseq_printf(seq, \" txtime_clockid: %s\\n\",\n+\t\t\t clockid == CLOCK_MONOTONIC ? \"monotonic\" :\n+\t\t\t clockid == CLOCK_REALTIME ? \"realtime\" :\n+\t\t\t clockid == CLOCK_TAI ? \"tai\" : \"unknown\");\n+\t}\n+\n \tif (pkt_dev-\u003exmit_mode == M_NETIF_RECEIVE)\n \t\tseq_puts(seq, \" xmit_mode: netif_receive\\n\");\n \telse if (pkt_dev-\u003exmit_mode == M_QUEUE_XMIT)\n@@ -1141,6 +1157,50 @@ static ssize_t pktgen_if_write(struct file *file,\n \t\t\t(unsigned long long) pkt_dev-\u003edelay);\n \t\treturn count;\n \t}\n+\tif (!strcmp(name, \"txtime_delay\")) {\n+\t\tmax = min(10, count - i);\n+\t\tlen = num_arg(\u0026user_buffer[i], max, \u0026value);\n+\t\tif (len \u003c 0)\n+\t\t\treturn len;\n+\n+\t\t/* in queue_xmit mode fq may clear tstamp, do not reuse skb */\n+\t\tif (value \u003e 0 \u0026\u0026 pkt_dev-\u003eclone_skb \u003e 0)\n+\t\t\treturn -EINVAL;\n+\n+\t\tWRITE_ONCE(pkt_dev-\u003etxtime_delay, (u64)value);\n+\t\tsprintf(pg_result, \"OK: txtime_delay=%llu\",\n+\t\t\t(unsigned long long)value);\n+\t\treturn count;\n+\t}\n+\tif (!strcmp(name, \"txtime_clockid\")) {\n+\t\tchar clockstr[32];\n+\t\tclockid_t clk;\n+\n+\t\tmemset(clockstr, 0, sizeof(clockstr));\n+\t\tmax = min(sizeof(clockstr) - 1, count - i);\n+\t\tlen = strn_len(\u0026user_buffer[i], max);\n+\t\tif (len \u003c 0)\n+\t\t\treturn len;\n+\n+\t\tif (copy_from_user(clockstr, \u0026user_buffer[i], len))\n+\t\t\treturn -EFAULT;\n+\n+\t\tif (!strcmp(clockstr, \"monotonic\")) {\n+\t\t\tclk = CLOCK_MONOTONIC;\n+\t\t} else if (!strcmp(clockstr, \"realtime\")) {\n+\t\t\tclk = CLOCK_REALTIME;\n+\t\t} else if (!strcmp(clockstr, \"tai\")) {\n+\t\t\tclk = CLOCK_TAI;\n+\t\t} else {\n+\t\t\tsprintf(pg_result, \"ERROR: unknown clockid '%s'\",\n+\t\t\t\tclockstr);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tsprintf(pg_result, \"OK: txtime_clockid=%s\", clockstr);\n+\t\tWRITE_ONCE(pkt_dev-\u003etxtime_clockid, clk);\n+\t\treturn count;\n+\t}\n \tif (!strcmp(name, \"rate\")) {\n \t\tmax = min(10, count - i);\n \t\tlen = num_arg(\u0026user_buffer[i], max, \u0026value);\n@@ -1238,6 +1298,8 @@ static ssize_t pktgen_if_write(struct file *file,\n \t\tif (value \u003e 0 \u0026\u0026 (pkt_dev-\u003en_imix_entries \u003e 0 ||\n \t\t\t\t !(pkt_dev-\u003eflags \u0026 F_SHARED)))\n \t\t\treturn -EINVAL;\n+\t\tif (value \u003e 0 \u0026\u0026 READ_ONCE(pkt_dev-\u003etxtime_delay))\n+\t\t\treturn -EINVAL;\n \n \t\tpkt_dev-\u003eclone_skb = value;\n \n@@ -2321,6 +2383,21 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)\n }\n \n \n+static ktime_t ktime_get_clock(clockid_t clockid)\n+{\n+\tswitch (clockid) {\n+\tcase CLOCK_REALTIME:\n+\t\treturn ktime_get_real();\n+\tcase CLOCK_MONOTONIC:\n+\t\treturn ktime_get();\n+\tcase CLOCK_TAI:\n+\t\treturn ktime_get_clocktai();\n+\tdefault:\n+\t\tWARN_ON_ONCE(1);\n+\t\treturn ktime_get();\n+\t}\n+}\n+\n static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)\n {\n \tktime_t start_time, end_time;\n@@ -3510,7 +3587,8 @@ static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)\n \n static void pktgen_xmit(struct pktgen_dev *pkt_dev)\n {\n-\tbool skb_shared = !!(READ_ONCE(pkt_dev-\u003eflags) \u0026 F_SHARED);\n+\tu32 flags = READ_ONCE(pkt_dev-\u003eflags);\n+\tbool skb_shared = !!(flags \u0026 F_SHARED);\n \tstruct net_device *odev = pkt_dev-\u003eodev;\n \tstruct netdev_queue *txq;\n \tunsigned int burst = 1;\n@@ -3545,6 +3623,8 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)\n \t/* If no skb or clone count exhausted then get new one */\n \tif (!pkt_dev-\u003eskb || (pkt_dev-\u003elast_ok \u0026\u0026\n \t\t\t ++pkt_dev-\u003eclone_count \u003e= clone_skb)) {\n+\t\tu64 txtime_delay;\n+\n \t\t/* build a new pkt */\n \t\tkfree_skb(pkt_dev-\u003eskb);\n \n@@ -3557,6 +3637,16 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)\n \t\t}\n \t\tpkt_dev-\u003elast_pkt_size = pkt_dev-\u003eskb-\u003elen;\n \t\tpkt_dev-\u003eclone_count = 0;\t/* reset counter */\n+\n+\t\ttxtime_delay = READ_ONCE(pkt_dev-\u003etxtime_delay);\n+\t\tif (flags \u0026 F_TXTIME \u0026\u0026 txtime_delay) {\n+\t\t\tclockid_t clk = READ_ONCE(pkt_dev-\u003etxtime_clockid);\n+\t\t\tktime_t txtime = ktime_add_ns(ktime_get_clock(clk),\n+\t\t\t\t\t\t txtime_delay);\n+\n+\t\t\tskb_set_delivery_type_by_clockid(pkt_dev-\u003eskb, txtime,\n+\t\t\t\t\t\t\t clk);\n+\t\t}\n \t}\n \n \tif (pkt_dev-\u003edelay \u0026\u0026 pkt_dev-\u003elast_ok)\n@@ -3869,6 +3959,7 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)\n \tpkt_dev-\u003eburst = 1;\n \tpkt_dev-\u003enode = NUMA_NO_NODE;\n \tpkt_dev-\u003eflags = F_SHARED;\t/* SKB shared by default */\n+\tpkt_dev-\u003etxtime_clockid = CLOCK_MONOTONIC;\n \n \terr = pktgen_setup_dev(t-\u003enet, pkt_dev, ifname);\n \tif (err)\ndiff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c\nindex 31c65a545a107c..2c7a245c5ffc2e 100644\n--- a/net/core/rtnetlink.c\n+++ b/net/core/rtnetlink.c\n@@ -2155,7 +2155,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,\n \t nla_put_u32(skb, IFLA_TSO_MAX_SEGS,\n \t\t\tREAD_ONCE(dev-\u003etso_max_segs)) ||\n \t nla_put_uint(skb, IFLA_MAX_PACING_OFFLOAD_HORIZON,\n-\t\t\t READ_ONCE(dev-\u003emax_pacing_offload_horizon)) ||\n+\t\t\t (u64)READ_ONCE(dev-\u003emax_pacing_offload_horizon) * NSEC_PER_USEC) ||\n #ifdef CONFIG_RPS\n \t nla_put_u32(skb, IFLA_NUM_RX_QUEUES,\n \t\t\tREAD_ONCE(dev-\u003enum_rx_queues)) ||\ndiff --git a/net/ethtool/common.c b/net/ethtool/common.c\nindex 23db40618fed14..9ca6980f2291e6 100644\n--- a/net/ethtool/common.c\n+++ b/net/ethtool/common.c\n@@ -954,6 +954,10 @@ void ethtool_ringparam_get_cfg(struct net_device *dev,\n \t/* Driver gives us current state, we want to return current config */\n \tkparam-\u003etcp_data_split = dev-\u003ecfg-\u003ehds_config;\n \tkparam-\u003ehds_thresh = dev-\u003ecfg-\u003ehds_thresh;\n+\n+\tkparam-\u003epacing_offload_horizon = READ_ONCE(dev-\u003epacing_offload_horizon);\n+\tkparam-\u003emax_pacing_offload_horizon =\n+\t\tREAD_ONCE(dev-\u003emax_pacing_offload_horizon);\n }\n \n static void ethtool_init_tsinfo(struct kernel_ethtool_ts_info *info)\ndiff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h\nindex 3e969a070f9fe4..ae7b651c658f74 100644\n--- a/net/ethtool/netlink.h\n+++ b/net/ethtool/netlink.h\n@@ -494,7 +494,7 @@ extern const struct nla_policy ethnl_features_set_policy[ETHTOOL_A_FEATURES_WANT\n extern const struct nla_policy ethnl_privflags_get_policy[ETHTOOL_A_PRIVFLAGS_HEADER + 1];\n extern const struct nla_policy ethnl_privflags_set_policy[ETHTOOL_A_PRIVFLAGS_FLAGS + 1];\n extern const struct nla_policy ethnl_rings_get_policy[ETHTOOL_A_RINGS_HEADER + 1];\n-extern const struct nla_policy ethnl_rings_set_policy[ETHTOOL_A_RINGS_HDS_THRESH_MAX + 1];\n+extern const struct nla_policy ethnl_rings_set_policy[ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON_MAX + 1];\n extern const struct nla_policy ethnl_channels_get_policy[ETHTOOL_A_CHANNELS_HEADER + 1];\n extern const struct nla_policy ethnl_channels_set_policy[ETHTOOL_A_CHANNELS_COMBINED_COUNT + 1];\n extern const struct nla_policy ethnl_coalesce_get_policy[ETHTOOL_A_COALESCE_HEADER + 1];\ndiff --git a/net/ethtool/rings.c b/net/ethtool/rings.c\nindex 9054c89c5d7bb2..5cc20d581c46ec 100644\n--- a/net/ethtool/rings.c\n+++ b/net/ethtool/rings.c\n@@ -42,6 +42,10 @@ static int rings_prepare_data(const struct ethnl_req_info *req_base,\n \n \tdata-\u003ekernel_ringparam.tcp_data_split = dev-\u003ecfg-\u003ehds_config;\n \tdata-\u003ekernel_ringparam.hds_thresh = dev-\u003ecfg-\u003ehds_thresh;\n+\tdata-\u003ekernel_ringparam.pacing_offload_horizon =\n+\t\tREAD_ONCE(dev-\u003epacing_offload_horizon);\n+\tdata-\u003ekernel_ringparam.max_pacing_offload_horizon =\n+\t\tREAD_ONCE(dev-\u003emax_pacing_offload_horizon);\n \n \tdev-\u003eethtool_ops-\u003eget_ringparam(dev, \u0026data-\u003eringparam,\n \t\t\t\t\t\u0026data-\u003ekernel_ringparam, info-\u003eextack);\n@@ -69,7 +73,9 @@ static int rings_reply_size(const struct ethnl_req_info *req_base,\n \t nla_total_size(sizeof(u32)) +\t/* _RINGS_TX_PUSH_BUF_LEN */\n \t nla_total_size(sizeof(u32)) +\t/* _RINGS_TX_PUSH_BUF_LEN_MAX */\n \t nla_total_size(sizeof(u32)) +\t/* _RINGS_HDS_THRESH */\n-\t nla_total_size(sizeof(u32));\t/* _RINGS_HDS_THRESH_MAX*/\n+\t nla_total_size(sizeof(u32)) +\t/* _RINGS_HDS_THRESH_MAX */\n+\t nla_total_size(sizeof(u32)) +\t/* _RINGS_PACING_OFFLOAD_HORIZON */\n+\t nla_total_size(sizeof(u32));\t/* _RINGS_PACING_OFFLOAD_HORIZON_MAX */\n }\n \n static int rings_fill_reply(struct sk_buff *skb,\n@@ -121,7 +127,12 @@ static int rings_fill_reply(struct sk_buff *skb,\n \t (nla_put_u32(skb, ETHTOOL_A_RINGS_HDS_THRESH,\n \t\t\t kr-\u003ehds_thresh) ||\n \t nla_put_u32(skb, ETHTOOL_A_RINGS_HDS_THRESH_MAX,\n-\t\t\t kr-\u003ehds_thresh_max))))\n+\t\t\t kr-\u003ehds_thresh_max))) ||\n+\t ((supported_ring_params \u0026 ETHTOOL_RING_USE_PACING_OFFLOAD_HORIZON) \u0026\u0026\n+\t (nla_put_u32(skb, ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON,\n+\t\t\t kr-\u003epacing_offload_horizon) ||\n+\t nla_put_u32(skb, ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON_MAX,\n+\t\t\t kr-\u003emax_pacing_offload_horizon))))\n \t\treturn -EMSGSIZE;\n \n \treturn 0;\n@@ -144,6 +155,7 @@ const struct nla_policy ethnl_rings_set_policy[] = {\n \t[ETHTOOL_A_RINGS_RX_PUSH]\t\t= NLA_POLICY_MAX(NLA_U8, 1),\n \t[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN]\t= { .type = NLA_U32 },\n \t[ETHTOOL_A_RINGS_HDS_THRESH]\t\t= { .type = NLA_U32 },\n+\t[ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON]\t= { .type = NLA_U32 },\n };\n \n static int\n@@ -177,6 +189,14 @@ ethnl_set_rings_validate(struct ethnl_req_info *req_info,\n \t\treturn -EOPNOTSUPP;\n \t}\n \n+\tif (tb[ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON] \u0026\u0026\n+\t !(ops-\u003esupported_ring_params \u0026 ETHTOOL_RING_USE_PACING_OFFLOAD_HORIZON)) {\n+\t\tNL_SET_ERR_MSG_ATTR(info-\u003eextack,\n+\t\t\t\t tb[ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON],\n+\t\t\t\t \"setting pacing offload horizon is not supported\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n \tif (tb[ETHTOOL_A_RINGS_CQE_SIZE] \u0026\u0026\n \t !(ops-\u003esupported_ring_params \u0026 ETHTOOL_RING_USE_CQE_SIZE)) {\n \t\tNL_SET_ERR_MSG_ATTR(info-\u003eextack,\n@@ -246,6 +266,8 @@ ethnl_set_rings(struct ethnl_req_info *req_info, struct genl_info *info)\n \t\t\t tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN], \u0026mod);\n \tethnl_update_u32(\u0026kernel_ringparam.hds_thresh,\n \t\t\t tb[ETHTOOL_A_RINGS_HDS_THRESH], \u0026mod);\n+\tethnl_update_u32(\u0026kernel_ringparam.pacing_offload_horizon,\n+\t\t\t tb[ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON], \u0026mod);\n \tif (!mod)\n \t\treturn 0;\n \n@@ -281,6 +303,9 @@ ethnl_set_rings(struct ethnl_req_info *req_info, struct genl_info *info)\n \t\terr_attr = tb[ETHTOOL_A_RINGS_TX];\n \telse if (kernel_ringparam.hds_thresh \u003e kernel_ringparam.hds_thresh_max)\n \t\terr_attr = tb[ETHTOOL_A_RINGS_HDS_THRESH];\n+\telse if (kernel_ringparam.pacing_offload_horizon \u003e\n+\t\t kernel_ringparam.max_pacing_offload_horizon)\n+\t\terr_attr = tb[ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON];\n \telse\n \t\terr_attr = NULL;\n \tif (err_attr) {\n@@ -302,6 +327,10 @@ ethnl_set_rings(struct ethnl_req_info *req_info, struct genl_info *info)\n \n \tret = dev-\u003eethtool_ops-\u003eset_ringparam(dev, \u0026ringparam,\n \t\t\t\t\t \u0026kernel_ringparam, info-\u003eextack);\n+\tif (!ret)\n+\t\tWRITE_ONCE(dev-\u003epacing_offload_horizon,\n+\t\t\t kernel_ringparam.pacing_offload_horizon);\n+\n \treturn ret \u003c 0 ? ret : 1;\n }\n \ndiff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c\nindex 7cae082a984721..cbf015df68ccf8 100644\n--- a/net/sched/sch_fq.c\n+++ b/net/sched/sch_fq.c\n@@ -399,6 +399,11 @@ static struct fq_flow *fq_classify(struct Qdisc *sch, struct sk_buff *skb,\n \t\t READ_ONCE(sk-\u003esk_pacing_status) != SK_PACING_FQ)\n \t\t\tsmp_store_release(\u0026sk-\u003esk_pacing_status,\n \t\t\t\t\t SK_PACING_FQ);\n+\n+\t\tif (q-\u003eoffload_horizon \u0026\u0026\n+\t\t fq_skb_cb(skb)-\u003etime_to_send \u003c= now)\n+\t\t\tskb-\u003etstamp = 0;\n+\n \t\treturn \u0026q-\u003einternal;\n \t}\n \n@@ -707,6 +712,7 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)\n \tstruct fq_sched_data *q = qdisc_priv(sch);\n \tstruct fq_perband_flows *pband;\n \tstruct fq_flow_head *head;\n+\tu64 time_next_packet;\n \tstruct sk_buff *skb;\n \tstruct fq_flow *f;\n \tunsigned long rate;\n@@ -721,7 +727,7 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)\n \tif (skb) {\n \t\tq-\u003einternal.qlen--;\n \t\tfq_dequeue_skb(sch, \u0026q-\u003einternal, skb);\n-\t\tgoto out;\n+\t\treturn skb;\n \t}\n \n \tnow = ktime_get_ns();\n@@ -758,8 +764,8 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)\n \n \tskb = fq_peek(f);\n \tif (skb) {\n-\t\tu64 time_next_packet = max_t(u64, fq_skb_cb(skb)-\u003etime_to_send,\n-\t\t\t\t\t f-\u003etime_next_packet);\n+\t\ttime_next_packet = max_t(u64, fq_skb_cb(skb)-\u003etime_to_send,\n+\t\t\t\t\t f-\u003etime_next_packet);\n \n \t\tif (now + q-\u003eoffload_horizon \u003c time_next_packet) {\n \t\t\thead-\u003efirst = f-\u003enext;\n@@ -828,11 +834,15 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)\n \t\t * f-\u003etime_next_packet was set when prior packet was sent,\n \t\t * and current time (@now) can be too late by tens of us.\n \t\t */\n-\t\tif (f-\u003etime_next_packet)\n+\t\tif (f-\u003etime_next_packet \u0026\u0026 f-\u003etime_next_packet \u003c now)\n \t\t\tlen -= min(len/2, now - f-\u003etime_next_packet);\n \t\tf-\u003etime_next_packet = now + len;\n \t}\n+\n out:\n+\tif (q-\u003eoffload_horizon \u0026\u0026 time_next_packet \u003c= now)\n+\t\tskb-\u003etstamp = 0;\n+\n \treturn skb;\n }\n \n@@ -1179,7 +1189,9 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt,\n \t\tu64 offload_horizon = (u64)NSEC_PER_USEC *\n \t\t\t\t nla_get_u32(tb[TCA_FQ_OFFLOAD_HORIZON]);\n \n-\t\tif (offload_horizon \u003c= qdisc_dev(sch)-\u003emax_pacing_offload_horizon) {\n+\t\tif (offload_horizon \u003c=\n+\t\t (u64)READ_ONCE(qdisc_dev(sch)-\u003epacing_offload_horizon) *\n+\t\t NSEC_PER_USEC) {\n \t\t\tWRITE_ONCE(q-\u003eoffload_horizon, offload_horizon);\n \t\t} else {\n \t\t\tNL_SET_ERR_MSG_MOD(extack, \"invalid offload_horizon\");\ndiff --git a/tools/testing/selftests/drivers/net/so_txtime.c b/tools/testing/selftests/drivers/net/so_txtime.c\nindex 55a386f3d1b90f..6e64baa4c60dd9 100644\n--- a/tools/testing/selftests/drivers/net/so_txtime.c\n+++ b/tools/testing/selftests/drivers/net/so_txtime.c\n@@ -42,8 +42,10 @@ static bool\tcfg_machine_slow;\n static uint64_t\tcfg_start_time_ns;\n static int\tcfg_mark;\n static bool\tcfg_rx;\n+static bool\tcfg_verify_hw_offload;\n \n static uint64_t glob_tstart;\n+static uint64_t glob_tstart_real;\n static uint64_t tdeliver_max;\n \n static int errors;\n@@ -158,18 +160,70 @@ static void do_recv_verify_empty(int fdr)\n \t\terror(1, 0, \"recv: not empty as expected (%d, %d)\", ret, errno);\n }\n \n-static int do_recv_errqueue_timeout(int fdt)\n+static int do_recv_errqueue_timeout(struct sock_extended_err *err,\n+\t\t\t\t const char payload_char)\n+{\n+\tconst char *reason = NULL;\n+\tint64_t tstamp = 0;\n+\n+\tswitch (err-\u003eee_errno) {\n+\tcase ECANCELED:\n+\t\tif (err-\u003eee_code != SO_EE_CODE_TXTIME_MISSED)\n+\t\t\terror(1, 0, \"errqueue: unknown ECANCELED %u\\n\",\n+\t\t\t err-\u003eee_code);\n+\t\treason = \"missed txtime\";\n+\tbreak;\n+\tcase EINVAL:\n+\t\tif (err-\u003eee_code != SO_EE_CODE_TXTIME_INVALID_PARAM)\n+\t\t\terror(1, 0, \"errqueue: unknown EINVAL %u\\n\",\n+\t\t\t err-\u003eee_code);\n+\t\treason = \"invalid txtime\";\n+\tbreak;\n+\tdefault:\n+\t\terror(1, 0, \"errqueue: errno %u code %u\\n\",\n+\t\t err-\u003eee_errno, err-\u003eee_code);\n+\t}\n+\n+\ttstamp = ((int64_t)err-\u003eee_data) \u003c\u003c 32 | err-\u003eee_info;\n+\ttstamp -= (int64_t)glob_tstart;\n+\ttstamp /= 1000 * 1000;\n+\tfprintf(stderr, \"send: pkt %c at %\" PRId64 \"ms dropped: %s\\n\",\n+\t\tpayload_char, tstamp, reason);\n+\n+\treturn 1;\n+}\n+\n+static void do_recv_errqueue_timestamping(struct sock_extended_err *err,\n+\t\t\t\t\t struct scm_timestamping *tss)\n+{\n+\tint64_t ts;\n+\n+\tts = tss-\u003ets[0].tv_sec;\n+\tts *= 1000ULL * 1000 * 1000;\n+\tts += tss-\u003ets[0].tv_nsec;\n+\tts -= glob_tstart_real;\n+\tts /= 1000;\n+\n+\tif (ts \u003e cfg_variance_us) {\n+\t\tfprintf(stderr, \"sw delay %\" PRId64 \"us exceeds bounds\\n\", ts);\n+\t\tif (!cfg_machine_slow)\n+\t\t\terrors++;\n+\t}\n+}\n+\n+static int do_recv_errqueue(int fdt)\n {\n \tchar control[CMSG_SPACE(sizeof(struct sock_extended_err)) +\n+\t\t CMSG_SPACE(sizeof(struct scm_timestamping)) +\n \t\t CMSG_SPACE(sizeof(struct sockaddr_in6))] = {0};\n \tchar data[sizeof(struct ethhdr) + sizeof(struct ipv6hdr) +\n \t\t sizeof(struct udphdr) + 1];\n+\tstruct scm_timestamping *tss;\n \tstruct sock_extended_err *err;\n \tint ret, num_tstamp = 0;\n \tstruct msghdr msg = {0};\n \tstruct iovec iov = {0};\n \tstruct cmsghdr *cm;\n-\tint64_t tstamp = 0;\n \n \tiov.iov_base = data;\n \tiov.iov_len = sizeof(data);\n@@ -181,8 +235,6 @@ static int do_recv_errqueue_timeout(int fdt)\n \tmsg.msg_controllen = sizeof(control);\n \n \twhile (1) {\n-\t\tconst char *reason = NULL;\n-\n \t\tret = recvmsg(fdt, \u0026msg, MSG_ERRQUEUE);\n \t\tif (ret == -1 \u0026\u0026 errno == EAGAIN)\n \t\t\tbreak;\n@@ -192,42 +244,32 @@ static int do_recv_errqueue_timeout(int fdt)\n \t\t\terror(1, 0, \"errqueue: flags 0x%x\\n\", msg.msg_flags);\n \n \t\tcm = CMSG_FIRSTHDR(\u0026msg);\n+\t\ttss = NULL;\n+\n+\t\tif (cm-\u003ecmsg_level == SOL_SOCKET \u0026\u0026\n+\t\t cm-\u003ecmsg_type == SCM_TIMESTAMPING) {\n+\t\t\ttss = (void *)CMSG_DATA(cm);\n+\t\t\tcm = CMSG_NXTHDR(\u0026msg, cm);\n+\t\t\tif (!cm)\n+\t\t\t\terror(1, 0, \"timestamp missing ip err\\n\");\n+\t\t}\n+\n \t\tif (cm-\u003ecmsg_level != cfg_errq_level ||\n \t\t cm-\u003ecmsg_type != cfg_errq_type)\n \t\t\terror(1, 0, \"errqueue: type 0x%x.0x%x\\n\",\n \t\t\t\t cm-\u003ecmsg_level, cm-\u003ecmsg_type);\n \n \t\terr = (struct sock_extended_err *)CMSG_DATA(cm);\n-\t\tif (err-\u003eee_origin != SO_EE_ORIGIN_TXTIME)\n+\t\tif (err-\u003eee_origin == SO_EE_ORIGIN_TXTIME)\n+\t\t\tnum_tstamp += do_recv_errqueue_timeout(err,\n+\t\t\t\t\t\t\t data[ret - 1]);\n+\t\telse if (err-\u003eee_origin == SO_EE_ORIGIN_TIMESTAMPING \u0026\u0026 tss)\n+\t\t\tdo_recv_errqueue_timestamping(err, tss);\n+\t\telse\n \t\t\terror(1, 0, \"errqueue: origin 0x%x\\n\", err-\u003eee_origin);\n \n-\t\tswitch (err-\u003eee_errno) {\n-\t\tcase ECANCELED:\n-\t\t\tif (err-\u003eee_code != SO_EE_CODE_TXTIME_MISSED)\n-\t\t\t\terror(1, 0, \"errqueue: unknown ECANCELED %u\\n\",\n-\t\t\t\t err-\u003eee_code);\n-\t\t\treason = \"missed txtime\";\n-\t\tbreak;\n-\t\tcase EINVAL:\n-\t\t\tif (err-\u003eee_code != SO_EE_CODE_TXTIME_INVALID_PARAM)\n-\t\t\t\terror(1, 0, \"errqueue: unknown EINVAL %u\\n\",\n-\t\t\t\t err-\u003eee_code);\n-\t\t\treason = \"invalid txtime\";\n-\t\tbreak;\n-\t\tdefault:\n-\t\t\terror(1, 0, \"errqueue: errno %u code %u\\n\",\n-\t\t\t err-\u003eee_errno, err-\u003eee_code);\n-\t\t}\n-\n-\t\ttstamp = ((int64_t) err-\u003eee_data) \u003c\u003c 32 | err-\u003eee_info;\n-\t\ttstamp -= (int64_t) glob_tstart;\n-\t\ttstamp /= 1000 * 1000;\n-\t\tfprintf(stderr, \"send: pkt %c at %\" PRId64 \"ms dropped: %s\\n\",\n-\t\t\tdata[ret - 1], tstamp, reason);\n-\n \t\tmsg.msg_flags = 0;\n \t\tmsg.msg_controllen = sizeof(control);\n-\t\tnum_tstamp++;\n \t}\n \n \treturn num_tstamp;\n@@ -245,12 +287,13 @@ static void recv_errqueue_msgs(int fdt)\n \t\t\terror(1, errno, \"poll\");\n \n \t\tif (ret \u0026\u0026 (pfd.revents \u0026 POLLERR))\n-\t\t\tnum_tstamp += do_recv_errqueue_timeout(fdt);\n+\t\t\tnum_tstamp += do_recv_errqueue(fdt);\n \n \t\tif (num_tstamp == cfg_num_pkt)\n \t\t\tbreak;\n \n-\t} while (gettime_ns(cfg_clockid) \u003c tdeliver_max);\n+\t} while (gettime_ns(cfg_clockid) \u003c\n+\t\t tdeliver_max + (cfg_variance_us * 1000));\n }\n \n static void start_time_wait(void)\n@@ -295,6 +338,18 @@ static void setsockopt_txtime(int fd)\n \t\terror(1, 0, \"getsockopt txtime: mismatch\");\n }\n \n+static void setsockopt_txtimestamping(int fd)\n+{\n+\tint val = SOF_TIMESTAMPING_TX_SOFTWARE |\n+\t\t SOF_TIMESTAMPING_SOFTWARE |\n+\t\t SOF_TIMESTAMPING_OPT_ID |\n+\t\t SOF_TIMESTAMPING_OPT_TSONLY;\n+\n+\tif (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,\n+\t\t \u0026val, sizeof(val)))\n+\t\terror(1, errno, \"setsockopt timestamping\");\n+}\n+\n static int setup_tx(struct sockaddr *addr, socklen_t alen)\n {\n \tint fd;\n@@ -308,6 +363,9 @@ static int setup_tx(struct sockaddr *addr, socklen_t alen)\n \n \tsetsockopt_txtime(fd);\n \n+\tif (cfg_verify_hw_offload)\n+\t\tsetsockopt_txtimestamping(fd);\n+\n \tif (cfg_mark \u0026\u0026\n \t setsockopt(fd, SOL_SOCKET, SO_MARK, \u0026cfg_mark, sizeof(cfg_mark)))\n \t\terror(1, errno, \"setsockopt mark\");\n@@ -317,7 +375,7 @@ static int setup_tx(struct sockaddr *addr, socklen_t alen)\n \n static int setup_rx(struct sockaddr *addr, socklen_t alen)\n {\n-\tstruct timeval tv = { .tv_usec = 100 * 1000 };\n+\tstruct timeval tv = { .tv_usec = 600 * 1000 };\n \tint fd;\n \n \tfd = socket(addr-\u003esa_family, SOCK_DGRAM, 0);\n@@ -348,6 +406,7 @@ static void do_test_tx(struct sockaddr *addr, socklen_t alen)\n \n \tstart_time_wait();\n \tglob_tstart = gettime_ns(cfg_clockid);\n+\tglob_tstart_real = gettime_ns(CLOCK_REALTIME);\n \n \tfor (i = 0; i \u003c cfg_num_pkt; i++)\n \t\tdo_send_one(fdt, \u0026cfg_buf[i]);\n@@ -440,10 +499,11 @@ static void usage(const char *progname)\n \t\t\t\" -6 only IPv6\\n\"\n \t\t\t\" -c \u003cclock\u003e monotonic or tai (default)\\n\"\n \t\t\t\" -D \u003caddr\u003e destination IP address (server)\\n\"\n-\t\t\t\" -S \u003caddr\u003e source IP address (client)\\n\"\n+\t\t\t\" -h verify hardware offload (tx)\\n\"\n+\t\t\t\" -m \u003cmark\u003e socket mark\\n\"\n \t\t\t\" -r run rx mode\\n\"\n+\t\t\t\" -S \u003caddr\u003e source IP address (client)\\n\"\n \t\t\t\" -t \u003cnsec\u003e start time (UTC nanoseconds)\\n\"\n-\t\t\t\" -m \u003cmark\u003e socket mark\\n\"\n \t\t\t\"\\n\",\n \t\t\tprogname);\n \texit(1);\n@@ -455,7 +515,7 @@ static void parse_opts(int argc, char **argv)\n \tint domain = PF_UNSPEC;\n \tint c;\n \n-\twhile ((c = getopt(argc, argv, \"46c:S:D:rt:m:\")) != -1) {\n+\twhile ((c = getopt(argc, argv, \"46c:D:hm:rS:t:\")) != -1) {\n \t\tswitch (c) {\n \t\tcase '4':\n \t\t\tif (domain != PF_UNSPEC)\n@@ -482,21 +542,24 @@ static void parse_opts(int argc, char **argv)\n \t\t\telse\n \t\t\t\terror(1, 0, \"unknown clock id %s\", optarg);\n \t\t\tbreak;\n-\t\tcase 'S':\n-\t\t\tsaddr = optarg;\n-\t\t\tbreak;\n \t\tcase 'D':\n \t\t\tdaddr = optarg;\n \t\t\tbreak;\n+\t\tcase 'h':\n+\t\t\tcfg_verify_hw_offload = true;\n+\t\t\tbreak;\n+\t\tcase 'm':\n+\t\t\tcfg_mark = strtol(optarg, NULL, 0);\n+\t\t\tbreak;\n \t\tcase 'r':\n \t\t\tcfg_rx = true;\n \t\t\tbreak;\n+\t\tcase 'S':\n+\t\t\tsaddr = optarg;\n+\t\t\tbreak;\n \t\tcase 't':\n \t\t\tcfg_start_time_ns = strtoll(optarg, NULL, 0);\n \t\t\tbreak;\n-\t\tcase 'm':\n-\t\t\tcfg_mark = strtol(optarg, NULL, 0);\n-\t\t\tbreak;\n \t\tdefault:\n \t\t\tusage(argv[0]);\n \t\t}\ndiff --git a/tools/testing/selftests/drivers/net/so_txtime.py b/tools/testing/selftests/drivers/net/so_txtime.py\nindex adf6c848d6d80f..7b75a33f5c43f8 100755\n--- a/tools/testing/selftests/drivers/net/so_txtime.py\n+++ b/tools/testing/selftests/drivers/net/so_txtime.py\n@@ -12,10 +12,13 @@ import time\n from lib.py import ksft_exit, ksft_run, ksft_variants\n from lib.py import KsftNamedVariant, KsftSkipEx\n from lib.py import NetDrvEpEnv, bkg, cmd, defer, tc\n+from lib.py import EthtoolFamily, NlError\n \n \n def test_so_txtime(cfg, clockid, ipver, args_tx, args_rx, expect_success):\n \"\"\"Main function. Run so_txtime as sender and receiver.\"\"\"\n+ cfg.require_ipver(ipver)\n+\n slow_machine = os.environ.get('KSFT_MACHINE_SLOW')\n \n if not hasattr(cfg, \"bin_remote\"):\n@@ -38,6 +41,34 @@ def test_so_txtime(cfg, clockid, ipver, args_tx, args_rx, expect_success):\n cmd(cmd_tx)\n \n \n+def _dev_setup_pacing_offload(cfg):\n+ \"\"\"Configure pacing-offload-horizon.\"\"\"\n+ ethnl = EthtoolFamily()\n+\n+ try:\n+ rings = ethnl.rings_get({'header': {'dev-index': cfg.ifindex}})\n+ except NlError as e:\n+ raise KsftSkipEx('ring-get not supported by device') from e\n+\n+ if 'pacing-offload-horizon' not in rings or \\\n+ 'pacing-offload-horizon-max' not in rings:\n+ raise KsftSkipEx('pacing offload horizon not supported by device')\n+\n+ if rings['pacing-offload-horizon-max'] \u003c 50_000:\n+ raise KsftSkipEx('pacing offload max horizon too small')\n+\n+ cur_horizon = rings['pacing-offload-horizon']\n+ new_horizon = 50_000\n+ ethnl.rings_set({\n+ 'header': {'dev-index': cfg.ifindex},\n+ 'pacing-offload-horizon': new_horizon,\n+ })\n+ defer(ethnl.rings_set, {\n+ 'header': {'dev-index': cfg.ifindex},\n+ 'pacing-offload-horizon': cur_horizon\n+ })\n+\n+\n def _qdisc_setup(ifname, qdisc, optargs=\"\"):\n \"\"\"Replace root qdisc. Restore the original after the test.\n \n@@ -56,6 +87,7 @@ def _test_variants_fq():\n [\"one_pkt\", \"a,10\", \"a,10\"],\n [\"in_order\", \"a,10,b,20\", \"a,10,b,20\"],\n [\"reverse_order\", \"a,20,b,10\", \"b,10,a,20\"],\n+ [\"beyond_hw_horizon\", \"a,70\", \"a,70\"],\n ]:\n name = f\"v{ipver}_{testcase[0]}\"\n yield KsftNamedVariant(name, ipver, testcase[1], testcase[2])\n@@ -64,15 +96,42 @@ def _test_variants_fq():\n @ksft_variants(_test_variants_fq())\n def test_so_txtime_fq_mono(cfg, ipver, args_tx, args_rx):\n \"\"\"Run all variants of monotonic (fq) tests.\"\"\"\n- cfg.require_ipver(ipver)\n _qdisc_setup(cfg.ifname, \"fq\")\n test_so_txtime(cfg, \"mono\", ipver, args_tx, args_rx, True)\n \n \n+@ksft_variants(_test_variants_fq())\n+def test_so_txtime_fq_mono_hw(cfg, ipver, args_tx, args_rx):\n+ \"\"\"Run all variants of monotonic fq tests, with offload horizon.\"\"\"\n+ cfg.require_nsim(nsim_test=False)\n+\n+ _dev_setup_pacing_offload(cfg)\n+ try:\n+ _qdisc_setup(cfg.ifname, \"fq\", \"offload_horizon 50ms\")\n+ except Exception as e:\n+ raise KsftSkipEx(\"netdev does not support offload. skipping\") from e\n+\n+ # Expect all tests to use only hw pacing, except beyond_hw_horizon.\n+ hw_only = \"-h\" if args_tx != \"a,70\" else \"\"\n+ test_so_txtime(cfg, \"mono\", ipver, f\"{hw_only} {args_tx}\", args_rx, True)\n+\n+\n+@ksft_variants(_test_variants_fq())\n+def test_so_txtime_pfifofast_mono_hw(cfg, ipver, args_tx, args_rx):\n+ \"\"\"Run all variants of monotonic tests, without fq pacing sw backup.\"\"\"\n+ cfg.require_nsim(nsim_test=False)\n+\n+ _dev_setup_pacing_offload(cfg)\n+ _qdisc_setup(cfg.ifname, \"pfifo_fast\")\n+\n+ # Expect all tests to pass, except beyond_hw_horizon without sw fallback.\n+ expect_pass = not args_tx == \"a,70\"\n+ test_so_txtime(cfg, \"mono\", ipver, f\"-h {args_tx}\", args_rx, expect_pass)\n+\n+\n @ksft_variants(_test_variants_fq())\n def test_so_txtime_fq_tai(cfg, ipver, args_tx, args_rx):\n \"\"\"Run all variants of fq tests, but pass CLOCK_TAI to test conversion.\"\"\"\n- cfg.require_ipver(ipver)\n _qdisc_setup(cfg.ifname, \"fq\")\n test_so_txtime(cfg, \"tai\", ipver, args_tx, args_rx, True)\n \n@@ -95,7 +154,6 @@ def _test_variants_etf():\n @ksft_variants(_test_variants_etf())\n def test_so_txtime_etf(cfg, ipver, args_tx, args_rx, expect_fail):\n \"\"\"Run all variants of etf tests.\"\"\"\n- cfg.require_ipver(ipver)\n try:\n _qdisc_setup(cfg.ifname, \"etf\", \"clockid CLOCK_TAI delta 400000\")\n except Exception as e:\n@@ -108,7 +166,13 @@ def main() -\u003e None:\n \"\"\"Boilerplate ksft main.\"\"\"\n with NetDrvEpEnv(__file__) as cfg:\n ksft_run(\n- [test_so_txtime_fq_mono, test_so_txtime_fq_tai, test_so_txtime_etf],\n+ [\n+ test_so_txtime_fq_mono,\n+ test_so_txtime_fq_mono_hw,\n+ test_so_txtime_pfifofast_mono_hw,\n+ test_so_txtime_fq_tai,\n+ test_so_txtime_etf,\n+ ],\n args=(cfg,),\n )\n ksft_exit()\n"
} |
| 2/1 | 2026/07/27 07:29 | agent | patch-evaluator |
1mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"NET_SCH_FQ",
"NET_PKTGEN",
"ETHTOOL_NETLINK"
],
"FocusSymbols": [
"fq_change",
"fq_dequeue",
"fq_classify",
"ethnl_set_rings",
"pktgen_if_write",
"pktgen_xmit"
],
"Reasoning": "The patch introduces a new feature for pacing offload horizon. It modifies the core Fair Queue packet scheduler (sch_fq.c), the packet generator (pktgen.c), and the ethtool netlink interface (rings.c) to support configuring and using this horizon. These core networking components are reachable and functional in virtualized environments, making them suitable for fuzzing. The changes to the idpf driver are hardware-specific and not emulated, but the core changes are sufficient to warrant 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 16d32b739584559558e964a2d703b79f2248b4ba
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Jul 27 07:29:29 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 5dd4d1b5d94bbb..c780684ca1bc0c 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -448,6 +448,12 @@ attribute-sets:
-
name: hds-thresh-max
type: u32
+ -
+ name: pacing-offload-horizon
+ type: u32
+ -
+ name: pacing-offload-horizon-max
+ type: u32
-
name: mm-stat
@@ -2168,6 +2174,8 @@ operations:
- tx-push-buf-len-max
- hds-thresh
- hds-thresh-max
+ - pacing-offload-horizon
+ - pacing-offload-horizon-max
dump: *ring-get-op
-
name: rings-set
diff --git a/Documentation/networking/net_cachelines/net_device.rst b/Documentation/networking/net_cachelines/net_device.rst
index 512f6d6fa3d8f8..3b9ce1c6e10578 100644
--- a/Documentation/networking/net_cachelines/net_device.rst
+++ b/Documentation/networking/net_cachelines/net_device.rst
@@ -183,7 +183,8 @@ struct devlink_port* devlink_port
struct dpll_pin* dpll_pin
struct hlist_head page_pools
struct dim_irq_moder* irq_moder
-u64 max_pacing_offload_horizon
+u32 pacing_offload_horizon
+u32 max_pacing_offload_horizon
struct_napi_config* napi_config
unsigned_long gro_flush_timeout
u32 napi_defer_hard_irqs
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c
index bb99d9e7c65d51..5ddf347927dc3e 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c
@@ -1801,7 +1801,8 @@ static void idpf_get_ts_stats(struct net_device *netdev,
static const struct ethtool_ops idpf_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_USE_ADAPTIVE,
- .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT,
+ .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT |
+ ETHTOOL_RING_USE_PACING_OFFLOAD_HORIZON,
.get_msglevel = idpf_get_msglevel,
.set_msglevel = idpf_set_msglevel,
.get_link = ethtool_op_get_link,
diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c
index cf966fe6c759ce..de4170c2aa3ba0 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c
@@ -888,6 +888,11 @@ static int idpf_cfg_netdev(struct idpf_vport *vport)
netdev->min_mtu = ETH_MIN_MTU;
netdev->max_mtu = vport->max_mtu;
+ if (idpf_is_queue_model_split(vport->dflt_qv_rsrc.txq_model) &&
+ !idpf_is_cap_ena(adapter, IDPF_OTHER_CAPS,
+ VIRTCHNL2_CAP_SPLITQ_QSCHED))
+ netdev->max_pacing_offload_horizon = 128000;
+
dflt_features = NETIF_F_SG |
NETIF_F_HIGHDMA;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
index 566b08ca3a6cb1..8f11de57c8bc77 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2023 Intel Corporation */
+#include <linux/netpoll.h>
+
#include "idpf.h"
#include "idpf_ptp.h"
#include "idpf_virtchnl.h"
@@ -2408,9 +2410,13 @@ void idpf_tx_splitq_build_flow_desc(union idpf_tx_flex_desc *desc,
struct idpf_tx_splitq_params *params,
u16 td_cmd, u16 size)
{
- *(u32 *)&desc->flow.qw1.cmd_dtype = (u8)(params->dtype | td_cmd);
+ *(__le32 *)&desc->flow.qw1.cmd_dtype = cpu_to_le32((u8)(params->dtype | td_cmd));
desc->flow.qw1.rxr_bufsize = cpu_to_le16((u16)size);
desc->flow.qw1.compl_tag = cpu_to_le16(params->compl_tag);
+
+ desc->flow.qw1.ts[0] = params->offload.desc_ts[0];
+ desc->flow.qw1.ts[1] = params->offload.desc_ts[1];
+ desc->flow.qw1.ts[2] = params->offload.desc_ts[2];
}
/**
@@ -3011,6 +3017,63 @@ static bool idpf_tx_splitq_need_re(struct idpf_tx_queue *tx_q)
return gap >= IDPF_TX_SPLITQ_RE_MIN_GAP;
}
+static void idpf_tx_splitq_set_txtime(const struct idpf_tx_queue *tx_q,
+ const struct sk_buff *skb,
+ struct idpf_tx_splitq_params *tx_params)
+{
+ const int ts_gran_pow2 = 9;
+ u64 ts, now, horizon;
+
+ horizon = READ_ONCE(skb->dev->pacing_offload_horizon);
+ if (!horizon)
+ return;
+
+ /* Skip if netpoll: not needed and not safe to call ktime helpers */
+ if (netpoll_tx_running(skb->dev))
+ return;
+
+ switch (skb->tstamp_type) {
+ case SKB_CLOCK_REALTIME:
+ ts = ktime_to_ns(ktime_add(skb->tstamp,
+ ktime_mono_to_any(0, TK_OFFS_TAI) -
+ ktime_mono_to_any(0, TK_OFFS_REAL)));
+ break;
+ case SKB_CLOCK_MONOTONIC:
+ ts = ktime_to_ns(ktime_mono_to_any(skb->tstamp, TK_OFFS_TAI));
+ break;
+ case SKB_CLOCK_TAI:
+ ts = ktime_to_ns(skb->tstamp);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return;
+ }
+
+ now = ktime_get_clocktai_ns();
+ if (ts < now)
+ return;
+
+ /* beyond offload horizon? set overflow bit only */
+ if (ts > now + (horizon * NSEC_PER_USEC)) {
+ tx_params->offload.desc_ts[2] = 1 << 7;
+ return;
+ }
+
+ ts >>= ts_gran_pow2;
+
+ /* 0 is valid 24b timestamp, but also means field unset.
+ * Add 512 ns (ts_gran_pow2) to avoid this case
+ */
+ if ((ts & 0x7fffff) == 0) {
+ tx_params->offload.desc_ts[0] = 1;
+ return;
+ }
+
+ tx_params->offload.desc_ts[0] = ts & 0xff;
+ tx_params->offload.desc_ts[1] = (ts >> 8) & 0xff;
+ tx_params->offload.desc_ts[2] = ((ts >> 16) & 0x7f);
+}
+
/**
* idpf_tx_splitq_frame - Sends buffer on Tx ring using flex descriptors
* @skb: send buffer
@@ -3097,6 +3160,10 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb,
tx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE;
tx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP;
+
+ if (skb->tstamp)
+ idpf_tx_splitq_set_txtime(tx_q, skb, &tx_params);
+
/* Set the RE bit to periodically "clean" the descriptor ring.
* MIN_GAP is set to MIN_RING size to ensure it will be set at
* least once each time around the ring.
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
index 4be5b3b6d3ed2b..865408b57b0d54 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
@@ -161,6 +161,7 @@ union idpf_tx_flex_desc {
* @tso_segs: Number of segments to be sent
* @tso_hdr_len: Length of headers to be duplicated
* @td_cmd: Command field to be inserted into descriptor
+ * @desc_ts: Flow scheduling offload timestamp
*/
struct idpf_tx_offload_params {
u32 tx_flags;
@@ -174,6 +175,7 @@ struct idpf_tx_offload_params {
u16 tso_hdr_len;
u16 td_cmd;
+ u8 desc_ts[3];
};
/**
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 5d491a98265ef9..adeebbf53dadea 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -85,6 +85,8 @@ enum {
* @tx_push_buf_max_len: Maximum allowed size of TX push buffer
* @hds_thresh: Packet size threshold for header data split (HDS)
* @hds_thresh_max: Maximum supported setting for @hds_threshold
+ * @pacing_offload_horizon: pacing offload horizon value in usec
+ * @max_pacing_offload_horizon: max pacing offload horizon value in usec
*
*/
struct kernel_ethtool_ringparam {
@@ -97,6 +99,8 @@ struct kernel_ethtool_ringparam {
u32 tx_push_buf_max_len;
u32 hds_thresh;
u32 hds_thresh_max;
+ u32 pacing_offload_horizon;
+ u32 max_pacing_offload_horizon;
};
/**
@@ -108,6 +112,7 @@ struct kernel_ethtool_ringparam {
* @ETHTOOL_RING_USE_TX_PUSH_BUF_LEN: capture for setting tx_push_buf_len
* @ETHTOOL_RING_USE_TCP_DATA_SPLIT: capture for setting tcp_data_split
* @ETHTOOL_RING_USE_HDS_THRS: capture for setting header-data-split-thresh
+ * @ETHTOOL_RING_USE_PACING_OFFLOAD_HORIZON: capture for setting pacing offload horizon
*/
enum ethtool_supported_ring_param {
ETHTOOL_RING_USE_RX_BUF_LEN = BIT(0),
@@ -117,6 +122,7 @@ enum ethtool_supported_ring_param {
ETHTOOL_RING_USE_TX_PUSH_BUF_LEN = BIT(4),
ETHTOOL_RING_USE_TCP_DATA_SPLIT = BIT(5),
ETHTOOL_RING_USE_HDS_THRS = BIT(6),
+ ETHTOOL_RING_USE_PACING_OFFLOAD_HORIZON = BIT(7),
};
#define __ETH_RSS_HASH_BIT(bit) ((u32)1 << (bit))
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 8db25b79573e3c..3c2ed45b55dcad 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2129,7 +2129,8 @@ enum netdev_reg_state {
* @dpll_pin: Pointer to the SyncE source pin of a DPLL subsystem,
* where the clock is recovered.
*
- * @max_pacing_offload_horizon: max EDT offload horizon in nsec.
+ * @pacing_offload_horizon: active pacing offload horizon in usec.
+ * @max_pacing_offload_horizon: max pacing offload horizon in usec.
* @napi_config: An array of napi_config structures containing per-NAPI
* settings.
* @num_napi_configs: number of allocated NAPI config structs,
@@ -2552,7 +2553,8 @@ struct net_device {
/** @irq_moder: dim parameters used if IS_ENABLED(CONFIG_DIMLIB). */
struct dim_irq_moder *irq_moder;
- u64 max_pacing_offload_horizon;
+ u32 pacing_offload_horizon;
+ u32 max_pacing_offload_horizon;
struct napi_config *napi_config;
u32 num_napi_configs;
u32 napi_defer_hard_irqs;
diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h
index 8134baf7860f3c..6f1a2fa36157d3 100644
--- a/include/uapi/linux/ethtool_netlink_generated.h
+++ b/include/uapi/linux/ethtool_netlink_generated.h
@@ -193,6 +193,8 @@ enum {
ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX,
ETHTOOL_A_RINGS_HDS_THRESH,
ETHTOOL_A_RINGS_HDS_THRESH_MAX,
+ ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON,
+ ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON_MAX,
__ETHTOOL_A_RINGS_CNT,
ETHTOOL_A_RINGS_MAX = (__ETHTOOL_A_RINGS_CNT - 1)
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 5b4dd04d612476..7897e422051300 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -201,6 +201,7 @@
pf(SVID_RND) /* Random SVLAN ID */ \
pf(NODE) /* Node memory alloc*/ \
pf(SHARED) /* Shared SKB */ \
+ pf(TXTIME) /* SO_TXTIME support */ \
#define pf(flag) flag##_SHIFT,
enum pkt_flags {
@@ -291,6 +292,10 @@ struct pktgen_dev {
struct page *page;
u64 delay; /* nano-seconds */
+ /* TXTIME support */
+ u64 txtime_delay; /* transmit time delay in ns */
+ clockid_t txtime_clockid; /* clockid for SO_TXTIME */
+
__u64 count; /* Default No packets to send */
__u64 sofar; /* How many pkts we've sent so far */
__u64 tx_bytes; /* How many bytes we've transmitted */
@@ -666,6 +671,17 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
if (pkt_dev->node >= 0)
seq_printf(seq, " node: %d\n", pkt_dev->node);
+ if (pkt_dev->flags & F_TXTIME) {
+ clockid_t clockid = READ_ONCE(pkt_dev->txtime_clockid);
+
+ seq_printf(seq, " txtime_delay: %llu\n",
+ (unsigned long long)pkt_dev->txtime_delay);
+ seq_printf(seq, " txtime_clockid: %s\n",
+ clockid == CLOCK_MONOTONIC ? "monotonic" :
+ clockid == CLOCK_REALTIME ? "realtime" :
+ clockid == CLOCK_TAI ? "tai" : "unknown");
+ }
+
if (pkt_dev->xmit_mode == M_NETIF_RECEIVE)
seq_puts(seq, " xmit_mode: netif_receive\n");
else if (pkt_dev->xmit_mode == M_QUEUE_XMIT)
@@ -1141,6 +1157,50 @@ static ssize_t pktgen_if_write(struct file *file,
(unsigned long long) pkt_dev->delay);
return count;
}
+ if (!strcmp(name, "txtime_delay")) {
+ max = min(10, count - i);
+ len = num_arg(&user_buffer[i], max, &value);
+ if (len < 0)
+ return len;
+
+ /* in queue_xmit mode fq may clear tstamp, do not reuse skb */
+ if (value > 0 && pkt_dev->clone_skb > 0)
+ return -EINVAL;
+
+ WRITE_ONCE(pkt_dev->txtime_delay, (u64)value);
+ sprintf(pg_result, "OK: txtime_delay=%llu",
+ (unsigned long long)value);
+ return count;
+ }
+ if (!strcmp(name, "txtime_clockid")) {
+ char clockstr[32];
+ clockid_t clk;
+
+ memset(clockstr, 0, sizeof(clockstr));
+ max = min(sizeof(clockstr) - 1, count - i);
+ len = strn_len(&user_buffer[i], max);
+ if (len < 0)
+ return len;
+
+ if (copy_from_user(clockstr, &user_buffer[i], len))
+ return -EFAULT;
+
+ if (!strcmp(clockstr, "monotonic")) {
+ clk = CLOCK_MONOTONIC;
+ } else if (!strcmp(clockstr, "realtime")) {
+ clk = CLOCK_REALTIME;
+ } else if (!strcmp(clockstr, "tai")) {
+ clk = CLOCK_TAI;
+ } else {
+ sprintf(pg_result, "ERROR: unknown clockid '%s'",
+ clockstr);
+ return -EINVAL;
+ }
+
+ sprintf(pg_result, "OK: txtime_clockid=%s", clockstr);
+ WRITE_ONCE(pkt_dev->txtime_clockid, clk);
+ return count;
+ }
if (!strcmp(name, "rate")) {
max = min(10, count - i);
len = num_arg(&user_buffer[i], max, &value);
@@ -1238,6 +1298,8 @@ static ssize_t pktgen_if_write(struct file *file,
if (value > 0 && (pkt_dev->n_imix_entries > 0 ||
!(pkt_dev->flags & F_SHARED)))
return -EINVAL;
+ if (value > 0 && READ_ONCE(pkt_dev->txtime_delay))
+ return -EINVAL;
pkt_dev->clone_skb = value;
@@ -2321,6 +2383,21 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
}
+static ktime_t ktime_get_clock(clockid_t clockid)
+{
+ switch (clockid) {
+ case CLOCK_REALTIME:
+ return ktime_get_real();
+ case CLOCK_MONOTONIC:
+ return ktime_get();
+ case CLOCK_TAI:
+ return ktime_get_clocktai();
+ default:
+ WARN_ON_ONCE(1);
+ return ktime_get();
+ }
+}
+
static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
{
ktime_t start_time, end_time;
@@ -3510,7 +3587,8 @@ static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)
static void pktgen_xmit(struct pktgen_dev *pkt_dev)
{
- bool skb_shared = !!(READ_ONCE(pkt_dev->flags) & F_SHARED);
+ u32 flags = READ_ONCE(pkt_dev->flags);
+ bool skb_shared = !!(flags & F_SHARED);
struct net_device *odev = pkt_dev->odev;
struct netdev_queue *txq;
unsigned int burst = 1;
@@ -3545,6 +3623,8 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
/* If no skb or clone count exhausted then get new one */
if (!pkt_dev->skb || (pkt_dev->last_ok &&
++pkt_dev->clone_count >= clone_skb)) {
+ u64 txtime_delay;
+
/* build a new pkt */
kfree_skb(pkt_dev->skb);
@@ -3557,6 +3637,16 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
}
pkt_dev->last_pkt_size = pkt_dev->skb->len;
pkt_dev->clone_count = 0; /* reset counter */
+
+ txtime_delay = READ_ONCE(pkt_dev->txtime_delay);
+ if (flags & F_TXTIME && txtime_delay) {
+ clockid_t clk = READ_ONCE(pkt_dev->txtime_clockid);
+ ktime_t txtime = ktime_add_ns(ktime_get_clock(clk),
+ txtime_delay);
+
+ skb_set_delivery_type_by_clockid(pkt_dev->skb, txtime,
+ clk);
+ }
}
if (pkt_dev->delay && pkt_dev->last_ok)
@@ -3869,6 +3959,7 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
pkt_dev->burst = 1;
pkt_dev->node = NUMA_NO_NODE;
pkt_dev->flags = F_SHARED; /* SKB shared by default */
+ pkt_dev->txtime_clockid = CLOCK_MONOTONIC;
err = pktgen_setup_dev(t->net, pkt_dev, ifname);
if (err)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 31c65a545a107c..2c7a245c5ffc2e 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2155,7 +2155,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
nla_put_u32(skb, IFLA_TSO_MAX_SEGS,
READ_ONCE(dev->tso_max_segs)) ||
nla_put_uint(skb, IFLA_MAX_PACING_OFFLOAD_HORIZON,
- READ_ONCE(dev->max_pacing_offload_horizon)) ||
+ (u64)READ_ONCE(dev->max_pacing_offload_horizon) * NSEC_PER_USEC) ||
#ifdef CONFIG_RPS
nla_put_u32(skb, IFLA_NUM_RX_QUEUES,
READ_ONCE(dev->num_rx_queues)) ||
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index 23db40618fed14..9ca6980f2291e6 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -954,6 +954,10 @@ void ethtool_ringparam_get_cfg(struct net_device *dev,
/* Driver gives us current state, we want to return current config */
kparam->tcp_data_split = dev->cfg->hds_config;
kparam->hds_thresh = dev->cfg->hds_thresh;
+
+ kparam->pacing_offload_horizon = READ_ONCE(dev->pacing_offload_horizon);
+ kparam->max_pacing_offload_horizon =
+ READ_ONCE(dev->max_pacing_offload_horizon);
}
static void ethtool_init_tsinfo(struct kernel_ethtool_ts_info *info)
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 3e969a070f9fe4..ae7b651c658f74 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -494,7 +494,7 @@ extern const struct nla_policy ethnl_features_set_policy[ETHTOOL_A_FEATURES_WANT
extern const struct nla_policy ethnl_privflags_get_policy[ETHTOOL_A_PRIVFLAGS_HEADER + 1];
extern const struct nla_policy ethnl_privflags_set_policy[ETHTOOL_A_PRIVFLAGS_FLAGS + 1];
extern const struct nla_policy ethnl_rings_get_policy[ETHTOOL_A_RINGS_HEADER + 1];
-extern const struct nla_policy ethnl_rings_set_policy[ETHTOOL_A_RINGS_HDS_THRESH_MAX + 1];
+extern const struct nla_policy ethnl_rings_set_policy[ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON_MAX + 1];
extern const struct nla_policy ethnl_channels_get_policy[ETHTOOL_A_CHANNELS_HEADER + 1];
extern const struct nla_policy ethnl_channels_set_policy[ETHTOOL_A_CHANNELS_COMBINED_COUNT + 1];
extern const struct nla_policy ethnl_coalesce_get_policy[ETHTOOL_A_COALESCE_HEADER + 1];
diff --git a/net/ethtool/rings.c b/net/ethtool/rings.c
index 9054c89c5d7bb2..5cc20d581c46ec 100644
--- a/net/ethtool/rings.c
+++ b/net/ethtool/rings.c
@@ -42,6 +42,10 @@ static int rings_prepare_data(const struct ethnl_req_info *req_base,
data->kernel_ringparam.tcp_data_split = dev->cfg->hds_config;
data->kernel_ringparam.hds_thresh = dev->cfg->hds_thresh;
+ data->kernel_ringparam.pacing_offload_horizon =
+ READ_ONCE(dev->pacing_offload_horizon);
+ data->kernel_ringparam.max_pacing_offload_horizon =
+ READ_ONCE(dev->max_pacing_offload_horizon);
dev->ethtool_ops->get_ringparam(dev, &data->ringparam,
&data->kernel_ringparam, info->extack);
@@ -69,7 +73,9 @@ static int rings_reply_size(const struct ethnl_req_info *req_base,
nla_total_size(sizeof(u32)) + /* _RINGS_TX_PUSH_BUF_LEN */
nla_total_size(sizeof(u32)) + /* _RINGS_TX_PUSH_BUF_LEN_MAX */
nla_total_size(sizeof(u32)) + /* _RINGS_HDS_THRESH */
- nla_total_size(sizeof(u32)); /* _RINGS_HDS_THRESH_MAX*/
+ nla_total_size(sizeof(u32)) + /* _RINGS_HDS_THRESH_MAX */
+ nla_total_size(sizeof(u32)) + /* _RINGS_PACING_OFFLOAD_HORIZON */
+ nla_total_size(sizeof(u32)); /* _RINGS_PACING_OFFLOAD_HORIZON_MAX */
}
static int rings_fill_reply(struct sk_buff *skb,
@@ -121,7 +127,12 @@ static int rings_fill_reply(struct sk_buff *skb,
(nla_put_u32(skb, ETHTOOL_A_RINGS_HDS_THRESH,
kr->hds_thresh) ||
nla_put_u32(skb, ETHTOOL_A_RINGS_HDS_THRESH_MAX,
- kr->hds_thresh_max))))
+ kr->hds_thresh_max))) ||
+ ((supported_ring_params & ETHTOOL_RING_USE_PACING_OFFLOAD_HORIZON) &&
+ (nla_put_u32(skb, ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON,
+ kr->pacing_offload_horizon) ||
+ nla_put_u32(skb, ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON_MAX,
+ kr->max_pacing_offload_horizon))))
return -EMSGSIZE;
return 0;
@@ -144,6 +155,7 @@ const struct nla_policy ethnl_rings_set_policy[] = {
[ETHTOOL_A_RINGS_RX_PUSH] = NLA_POLICY_MAX(NLA_U8, 1),
[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN] = { .type = NLA_U32 },
[ETHTOOL_A_RINGS_HDS_THRESH] = { .type = NLA_U32 },
+ [ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON] = { .type = NLA_U32 },
};
static int
@@ -177,6 +189,14 @@ ethnl_set_rings_validate(struct ethnl_req_info *req_info,
return -EOPNOTSUPP;
}
+ if (tb[ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON] &&
+ !(ops->supported_ring_params & ETHTOOL_RING_USE_PACING_OFFLOAD_HORIZON)) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ tb[ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON],
+ "setting pacing offload horizon is not supported");
+ return -EOPNOTSUPP;
+ }
+
if (tb[ETHTOOL_A_RINGS_CQE_SIZE] &&
!(ops->supported_ring_params & ETHTOOL_RING_USE_CQE_SIZE)) {
NL_SET_ERR_MSG_ATTR(info->extack,
@@ -246,6 +266,8 @@ ethnl_set_rings(struct ethnl_req_info *req_info, struct genl_info *info)
tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN], &mod);
ethnl_update_u32(&kernel_ringparam.hds_thresh,
tb[ETHTOOL_A_RINGS_HDS_THRESH], &mod);
+ ethnl_update_u32(&kernel_ringparam.pacing_offload_horizon,
+ tb[ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON], &mod);
if (!mod)
return 0;
@@ -281,6 +303,9 @@ ethnl_set_rings(struct ethnl_req_info *req_info, struct genl_info *info)
err_attr = tb[ETHTOOL_A_RINGS_TX];
else if (kernel_ringparam.hds_thresh > kernel_ringparam.hds_thresh_max)
err_attr = tb[ETHTOOL_A_RINGS_HDS_THRESH];
+ else if (kernel_ringparam.pacing_offload_horizon >
+ kernel_ringparam.max_pacing_offload_horizon)
+ err_attr = tb[ETHTOOL_A_RINGS_PACING_OFFLOAD_HORIZON];
else
err_attr = NULL;
if (err_attr) {
@@ -302,6 +327,10 @@ ethnl_set_rings(struct ethnl_req_info *req_info, struct genl_info *info)
ret = dev->ethtool_ops->set_ringparam(dev, &ringparam,
&kernel_ringparam, info->extack);
+ if (!ret)
+ WRITE_ONCE(dev->pacing_offload_horizon,
+ kernel_ringparam.pacing_offload_horizon);
+
return ret < 0 ? ret : 1;
}
diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c
index 7cae082a984721..cbf015df68ccf8 100644
--- a/net/sched/sch_fq.c
+++ b/net/sched/sch_fq.c
@@ -399,6 +399,11 @@ static struct fq_flow *fq_classify(struct Qdisc *sch, struct sk_buff *skb,
READ_ONCE(sk->sk_pacing_status) != SK_PACING_FQ)
smp_store_release(&sk->sk_pacing_status,
SK_PACING_FQ);
+
+ if (q->offload_horizon &&
+ fq_skb_cb(skb)->time_to_send <= now)
+ skb->tstamp = 0;
+
return &q->internal;
}
@@ -707,6 +712,7 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)
struct fq_sched_data *q = qdisc_priv(sch);
struct fq_perband_flows *pband;
struct fq_flow_head *head;
+ u64 time_next_packet;
struct sk_buff *skb;
struct fq_flow *f;
unsigned long rate;
@@ -721,7 +727,7 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)
if (skb) {
q->internal.qlen--;
fq_dequeue_skb(sch, &q->internal, skb);
- goto out;
+ return skb;
}
now = ktime_get_ns();
@@ -758,8 +764,8 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)
skb = fq_peek(f);
if (skb) {
- u64 time_next_packet = max_t(u64, fq_skb_cb(skb)->time_to_send,
- f->time_next_packet);
+ time_next_packet = max_t(u64, fq_skb_cb(skb)->time_to_send,
+ f->time_next_packet);
if (now + q->offload_horizon < time_next_packet) {
head->first = f->next;
@@ -828,11 +834,15 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)
* f->time_next_packet was set when prior packet was sent,
* and current time (@now) can be too late by tens of us.
*/
- if (f->time_next_packet)
+ if (f->time_next_packet && f->time_next_packet < now)
len -= min(len/2, now - f->time_next_packet);
f->time_next_packet = now + len;
}
+
out:
+ if (q->offload_horizon && time_next_packet <= now)
+ skb->tstamp = 0;
+
return skb;
}
@@ -1179,7 +1189,9 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt,
u64 offload_horizon = (u64)NSEC_PER_USEC *
nla_get_u32(tb[TCA_FQ_OFFLOAD_HORIZON]);
- if (offload_horizon <= qdisc_dev(sch)->max_pacing_offload_horizon) {
+ if (offload_horizon <=
+ (u64)READ_ONCE(qdisc_dev(sch)->pacing_offload_horizon) *
+ NSEC_PER_USEC) {
WRITE_ONCE(q->offload_horizon, offload_horizon);
} else {
NL_SET_ERR_MSG_MOD(extack, "invalid offload_horizon");
diff --git a/tools/testing/selftests/drivers/net/so_txtime.c b/tools/testing/selftests/drivers/net/so_txtime.c
index 55a386f3d1b90f..6e64baa4c60dd9 100644
--- a/tools/testing/selftests/drivers/net/so_txtime.c
+++ b/tools/testing/selftests/drivers/net/so_txtime.c
@@ -42,8 +42,10 @@ static bool cfg_machine_slow;
static uint64_t cfg_start_time_ns;
static int cfg_mark;
static bool cfg_rx;
+static bool cfg_verify_hw_offload;
static uint64_t glob_tstart;
+static uint64_t glob_tstart_real;
static uint64_t tdeliver_max;
static int errors;
@@ -158,18 +160,70 @@ static void do_recv_verify_empty(int fdr)
error(1, 0, "recv: not empty as expected (%d, %d)", ret, errno);
}
-static int do_recv_errqueue_timeout(int fdt)
+static int do_recv_errqueue_timeout(struct sock_extended_err *err,
+ const char payload_char)
+{
+ const char *reason = NULL;
+ int64_t tstamp = 0;
+
+ switch (err->ee_errno) {
+ case ECANCELED:
+ if (err->ee_code != SO_EE_CODE_TXTIME_MISSED)
+ error(1, 0, "errqueue: unknown ECANCELED %u\n",
+ err->ee_code);
+ reason = "missed txtime";
+ break;
+ case EINVAL:
+ if (err->ee_code != SO_EE_CODE_TXTIME_INVALID_PARAM)
+ error(1, 0, "errqueue: unknown EINVAL %u\n",
+ err->ee_code);
+ reason = "invalid txtime";
+ break;
+ default:
+ error(1, 0, "errqueue: errno %u code %u\n",
+ err->ee_errno, err->ee_code);
+ }
+
+ tstamp = ((int64_t)err->ee_data) << 32 | err->ee_info;
+ tstamp -= (int64_t)glob_tstart;
+ tstamp /= 1000 * 1000;
+ fprintf(stderr, "send: pkt %c at %" PRId64 "ms dropped: %s\n",
+ payload_char, tstamp, reason);
+
+ return 1;
+}
+
+static void do_recv_errqueue_timestamping(struct sock_extended_err *err,
+ struct scm_timestamping *tss)
+{
+ int64_t ts;
+
+ ts = tss->ts[0].tv_sec;
+ ts *= 1000ULL * 1000 * 1000;
+ ts += tss->ts[0].tv_nsec;
+ ts -= glob_tstart_real;
+ ts /= 1000;
+
+ if (ts > cfg_variance_us) {
+ fprintf(stderr, "sw delay %" PRId64 "us exceeds bounds\n", ts);
+ if (!cfg_machine_slow)
+ errors++;
+ }
+}
+
+static int do_recv_errqueue(int fdt)
{
char control[CMSG_SPACE(sizeof(struct sock_extended_err)) +
+ CMSG_SPACE(sizeof(struct scm_timestamping)) +
CMSG_SPACE(sizeof(struct sockaddr_in6))] = {0};
char data[sizeof(struct ethhdr) + sizeof(struct ipv6hdr) +
sizeof(struct udphdr) + 1];
+ struct scm_timestamping *tss;
struct sock_extended_err *err;
int ret, num_tstamp = 0;
struct msghdr msg = {0};
struct iovec iov = {0};
struct cmsghdr *cm;
- int64_t tstamp = 0;
iov.iov_base = data;
iov.iov_len = sizeof(data);
@@ -181,8 +235,6 @@ static int do_recv_errqueue_timeout(int fdt)
msg.msg_controllen = sizeof(control);
while (1) {
- const char *reason = NULL;
-
ret = recvmsg(fdt, &msg, MSG_ERRQUEUE);
if (ret == -1 && errno == EAGAIN)
break;
@@ -192,42 +244,32 @@ static int do_recv_errqueue_timeout(int fdt)
error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags);
cm = CMSG_FIRSTHDR(&msg);
+ tss = NULL;
+
+ if (cm->cmsg_level == SOL_SOCKET &&
+ cm->cmsg_type == SCM_TIMESTAMPING) {
+ tss = (void *)CMSG_DATA(cm);
+ cm = CMSG_NXTHDR(&msg, cm);
+ if (!cm)
+ error(1, 0, "timestamp missing ip err\n");
+ }
+
if (cm->cmsg_level != cfg_errq_level ||
cm->cmsg_type != cfg_errq_type)
error(1, 0, "errqueue: type 0x%x.0x%x\n",
cm->cmsg_level, cm->cmsg_type);
err = (struct sock_extended_err *)CMSG_DATA(cm);
- if (err->ee_origin != SO_EE_ORIGIN_TXTIME)
+ if (err->ee_origin == SO_EE_ORIGIN_TXTIME)
+ num_tstamp += do_recv_errqueue_timeout(err,
+ data[ret - 1]);
+ else if (err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING && tss)
+ do_recv_errqueue_timestamping(err, tss);
+ else
error(1, 0, "errqueue: origin 0x%x\n", err->ee_origin);
- switch (err->ee_errno) {
- case ECANCELED:
- if (err->ee_code != SO_EE_CODE_TXTIME_MISSED)
- error(1, 0, "errqueue: unknown ECANCELED %u\n",
- err->ee_code);
- reason = "missed txtime";
- break;
- case EINVAL:
- if (err->ee_code != SO_EE_CODE_TXTIME_INVALID_PARAM)
- error(1, 0, "errqueue: unknown EINVAL %u\n",
- err->ee_code);
- reason = "invalid txtime";
- break;
- default:
- error(1, 0, "errqueue: errno %u code %u\n",
- err->ee_errno, err->ee_code);
- }
-
- tstamp = ((int64_t) err->ee_data) << 32 | err->ee_info;
- tstamp -= (int64_t) glob_tstart;
- tstamp /= 1000 * 1000;
- fprintf(stderr, "send: pkt %c at %" PRId64 "ms dropped: %s\n",
- data[ret - 1], tstamp, reason);
-
msg.msg_flags = 0;
msg.msg_controllen = sizeof(control);
- num_tstamp++;
}
return num_tstamp;
@@ -245,12 +287,13 @@ static void recv_errqueue_msgs(int fdt)
error(1, errno, "poll");
if (ret && (pfd.revents & POLLERR))
- num_tstamp += do_recv_errqueue_timeout(fdt);
+ num_tstamp += do_recv_errqueue(fdt);
if (num_tstamp == cfg_num_pkt)
break;
- } while (gettime_ns(cfg_clockid) < tdeliver_max);
+ } while (gettime_ns(cfg_clockid) <
+ tdeliver_max + (cfg_variance_us * 1000));
}
static void start_time_wait(void)
@@ -295,6 +338,18 @@ static void setsockopt_txtime(int fd)
error(1, 0, "getsockopt txtime: mismatch");
}
+static void setsockopt_txtimestamping(int fd)
+{
+ int val = SOF_TIMESTAMPING_TX_SOFTWARE |
+ SOF_TIMESTAMPING_SOFTWARE |
+ SOF_TIMESTAMPING_OPT_ID |
+ SOF_TIMESTAMPING_OPT_TSONLY;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
+ &val, sizeof(val)))
+ error(1, errno, "setsockopt timestamping");
+}
+
static int setup_tx(struct sockaddr *addr, socklen_t alen)
{
int fd;
@@ -308,6 +363,9 @@ static int setup_tx(struct sockaddr *addr, socklen_t alen)
setsockopt_txtime(fd);
+ if (cfg_verify_hw_offload)
+ setsockopt_txtimestamping(fd);
+
if (cfg_mark &&
setsockopt(fd, SOL_SOCKET, SO_MARK, &cfg_mark, sizeof(cfg_mark)))
error(1, errno, "setsockopt mark");
@@ -317,7 +375,7 @@ static int setup_tx(struct sockaddr *addr, socklen_t alen)
static int setup_rx(struct sockaddr *addr, socklen_t alen)
{
- struct timeval tv = { .tv_usec = 100 * 1000 };
+ struct timeval tv = { .tv_usec = 600 * 1000 };
int fd;
fd = socket(addr->sa_family, SOCK_DGRAM, 0);
@@ -348,6 +406,7 @@ static void do_test_tx(struct sockaddr *addr, socklen_t alen)
start_time_wait();
glob_tstart = gettime_ns(cfg_clockid);
+ glob_tstart_real = gettime_ns(CLOCK_REALTIME);
for (i = 0; i < cfg_num_pkt; i++)
do_send_one(fdt, &cfg_buf[i]);
@@ -440,10 +499,11 @@ static void usage(const char *progname)
" -6 only IPv6\n"
" -c <clock> monotonic or tai (default)\n"
" -D <addr> destination IP address (server)\n"
- " -S <addr> source IP address (client)\n"
+ " -h verify hardware offload (tx)\n"
+ " -m <mark> socket mark\n"
" -r run rx mode\n"
+ " -S <addr> source IP address (client)\n"
" -t <nsec> start time (UTC nanoseconds)\n"
- " -m <mark> socket mark\n"
"\n",
progname);
exit(1);
@@ -455,7 +515,7 @@ static void parse_opts(int argc, char **argv)
int domain = PF_UNSPEC;
int c;
- while ((c = getopt(argc, argv, "46c:S:D:rt:m:")) != -1) {
+ while ((c = getopt(argc, argv, "46c:D:hm:rS:t:")) != -1) {
switch (c) {
case '4':
if (domain != PF_UNSPEC)
@@ -482,21 +542,24 @@ static void parse_opts(int argc, char **argv)
else
error(1, 0, "unknown clock id %s", optarg);
break;
- case 'S':
- saddr = optarg;
- break;
case 'D':
daddr = optarg;
break;
+ case 'h':
+ cfg_verify_hw_offload = true;
+ break;
+ case 'm':
+ cfg_mark = strtol(optarg, NULL, 0);
+ break;
case 'r':
cfg_rx = true;
break;
+ case 'S':
+ saddr = optarg;
+ break;
case 't':
cfg_start_time_ns = strtoll(optarg, NULL, 0);
break;
- case 'm':
- cfg_mark = strtol(optarg, NULL, 0);
- break;
default:
usage(argv[0]);
}
diff --git a/tools/testing/selftests/drivers/net/so_txtime.py b/tools/testing/selftests/drivers/net/so_txtime.py
index adf6c848d6d80f..7b75a33f5c43f8 100755
--- a/tools/testing/selftests/drivers/net/so_txtime.py
+++ b/tools/testing/selftests/drivers/net/so_txtime.py
@@ -12,10 +12,13 @@ import time
from lib.py import ksft_exit, ksft_run, ksft_variants
from lib.py import KsftNamedVariant, KsftSkipEx
from lib.py import NetDrvEpEnv, bkg, cmd, defer, tc
+from lib.py import EthtoolFamily, NlError
def test_so_txtime(cfg, clockid, ipver, args_tx, args_rx, expect_success):
"""Main function. Run so_txtime as sender and receiver."""
+ cfg.require_ipver(ipver)
+
slow_machine = os.environ.get('KSFT_MACHINE_SLOW')
if not hasattr(cfg, "bin_remote"):
@@ -38,6 +41,34 @@ def test_so_txtime(cfg, clockid, ipver, args_tx, args_rx, expect_success):
cmd(cmd_tx)
+def _dev_setup_pacing_offload(cfg):
+ """Configure pacing-offload-horizon."""
+ ethnl = EthtoolFamily()
+
+ try:
+ rings = ethnl.rings_get({'header': {'dev-index': cfg.ifindex}})
+ except NlError as e:
+ raise KsftSkipEx('ring-get not supported by device') from e
+
+ if 'pacing-offload-horizon' not in rings or \
+ 'pacing-offload-horizon-max' not in rings:
+ raise KsftSkipEx('pacing offload horizon not supported by device')
+
+ if rings['pacing-offload-horizon-max'] < 50_000:
+ raise KsftSkipEx('pacing offload max horizon too small')
+
+ cur_horizon = rings['pacing-offload-horizon']
+ new_horizon = 50_000
+ ethnl.rings_set({
+ 'header': {'dev-index': cfg.ifindex},
+ 'pacing-offload-horizon': new_horizon,
+ })
+ defer(ethnl.rings_set, {
+ 'header': {'dev-index': cfg.ifindex},
+ 'pacing-offload-horizon': cur_horizon
+ })
+
+
def _qdisc_setup(ifname, qdisc, optargs=""):
"""Replace root qdisc. Restore the original after the test.
@@ -56,6 +87,7 @@ def _test_variants_fq():
["one_pkt", "a,10", "a,10"],
["in_order", "a,10,b,20", "a,10,b,20"],
["reverse_order", "a,20,b,10", "b,10,a,20"],
+ ["beyond_hw_horizon", "a,70", "a,70"],
]:
name = f"v{ipver}_{testcase[0]}"
yield KsftNamedVariant(name, ipver, testcase[1], testcase[2])
@@ -64,15 +96,42 @@ def _test_variants_fq():
@ksft_variants(_test_variants_fq())
def test_so_txtime_fq_mono(cfg, ipver, args_tx, args_rx):
"""Run all variants of monotonic (fq) tests."""
- cfg.require_ipver(ipver)
_qdisc_setup(cfg.ifname, "fq")
test_so_txtime(cfg, "mono", ipver, args_tx, args_rx, True)
+@ksft_variants(_test_variants_fq())
+def test_so_txtime_fq_mono_hw(cfg, ipver, args_tx, args_rx):
+ """Run all variants of monotonic fq tests, with offload horizon."""
+ cfg.require_nsim(nsim_test=False)
+
+ _dev_setup_pacing_offload(cfg)
+ try:
+ _qdisc_setup(cfg.ifname, "fq", "offload_horizon 50ms")
+ except Exception as e:
+ raise KsftSkipEx("netdev does not support offload. skipping") from e
+
+ # Expect all tests to use only hw pacing, except beyond_hw_horizon.
+ hw_only = "-h" if args_tx != "a,70" else ""
+ test_so_txtime(cfg, "mono", ipver, f"{hw_only} {args_tx}", args_rx, True)
+
+
+@ksft_variants(_test_variants_fq())
+def test_so_txtime_pfifofast_mono_hw(cfg, ipver, args_tx, args_rx):
+ """Run all variants of monotonic tests, without fq pacing sw backup."""
+ cfg.require_nsim(nsim_test=False)
+
+ _dev_setup_pacing_offload(cfg)
+ _qdisc_setup(cfg.ifname, "pfifo_fast")
+
+ # Expect all tests to pass, except beyond_hw_horizon without sw fallback.
+ expect_pass = not args_tx == "a,70"
+ test_so_txtime(cfg, "mono", ipver, f"-h {args_tx}", args_rx, expect_pass)
+
+
@ksft_variants(_test_variants_fq())
def test_so_txtime_fq_tai(cfg, ipver, args_tx, args_rx):
"""Run all variants of fq tests, but pass CLOCK_TAI to test conversion."""
- cfg.require_ipver(ipver)
_qdisc_setup(cfg.ifname, "fq")
test_so_txtime(cfg, "tai", ipver, args_tx, args_rx, True)
@@ -95,7 +154,6 @@ def _test_variants_etf():
@ksft_variants(_test_variants_etf())
def test_so_txtime_etf(cfg, ipver, args_tx, args_rx, expect_fail):
"""Run all variants of etf tests."""
- cfg.require_ipver(ipver)
try:
_qdisc_setup(cfg.ifname, "etf", "clockid CLOCK_TAI delta 400000")
except Exception as e:
@@ -108,7 +166,13 @@ def main() -> None:
"""Boilerplate ksft main."""
with NetDrvEpEnv(__file__) as cfg:
ksft_run(
- [test_so_txtime_fq_mono, test_so_txtime_fq_tai, test_so_txtime_etf],
+ [
+ test_so_txtime_fq_mono,
+ test_so_txtime_fq_mono_hw,
+ test_so_txtime_pfifofast_mono_hw,
+ test_so_txtime_fq_tai,
+ test_so_txtime_etf,
+ ],
args=(cfg,),
)
ksft_exit()
|
| 3/2 | 2026/07/27 07:29 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17819 output: 31 thoughts: 948 Thoughts: **Analyzing the Pacing Offload Horizon Patch** Okay, so we have a new patch introducing the "pacing offload horizon" feature. My initial scan shows this thing touches a pretty wide range of files. It's modifying documentation, several core networking components, and drivers for the Intel `idpf` driver. Specifically, it's updating `Documentation/netlink/specs/ethtool.yaml`, `Documentation/networking/net_cachelines/net_device.rst`, parts of the `idpf` driver, and key files in core networking: `net/sched/sch_fq.c`, `net/core/pktgen.c`, `net/ethtool/rings.c`, and more. This is immediately interesting because it's integrating directly with core scheduling, packet generation, and `ethtool`, which means this could impact performance and behavior in some significant ways. The patch introduces `pacing_offload_horizon` and `max_pacing_offload_horizon` to `struct net_device`. Looking deeper, the changes in `net/sched/sch_fq.c` seem to be the core of the feature, using `q->offload_horizon` which is configured via `TCA_FQ_OFFLOAD_HORIZON` and checked against `qdisc_dev(sch)->pacing_offload_horizon`. Also, `net/core/pktgen.c` now has support for `TXTIME`, and `net/ethtool/rings.c` is being modified to allow setting the `pacing_offload_horizon`. Now, the important question is: is this reachable? `sch_fq.c` is the Fair Queue packet scheduler, and that's *widely* used. It's certainly reachable in virtualized environments, which I know matters a lot to my current work. Similarly, `pktgen.c` is the packet generator, also accessible. Ethtool core is, of course, reachable. That's good. Let's dive into the core changes: Looking at `net/sched/sch_fq.c`, the modified functions are `fq_classify`, `fq_dequeue`, and `fq_change`. In `net/core/pktgen.c`, it's `pktgen_if_show`, `pktgen_if_write`, `pktgen_setup_inject`, `ktime_get_clock`, `pktgen_xmit`, and `pktgen_add_device`. And for `net/ethtool/rings.c` it's `rings_prepare_data`, `rings_reply_size`, `rings_fill_reply`, `ethnl_set_rings_validate`, and `ethnl_set_rings`. Additionally, there are changes in `net/core/rtnetlink.c` ( `rtnl_fill_ifinfo`) and `net/ethtool/common.c` ( `ethtool_ringparam_get_cfg`). So, the biggest impact is on `sch_fq.c`, `pktgen.c`, and `ethtool`, so I'm thinking this deserves some serious fuzzing. Especially these symbols: `fq_classify`, `fq_dequeue`, `fq_change`, `pktgen_if_write`, `pktgen_xmit`, and `ethnl_set_rings`. Now, let's verify the configs. For `sch_fq.c`, it's `CONFIG_NET_SCH_FQ`. For `pktgen.c`, it's `CONFIG_NET_PKTGEN`. And for `ethtool`, it's always enabled with `CONFIG_NET`. I'll use `grepper` to ensure I have the exact names. Okay, this looks like a solid starting point for a deeper investigation. |
| 4/2 | 2026/07/27 07:29 | tool | grepper |
0mArgs:{
"Expression": "NET_SCH_FQ",
"PathPrefix": "net/sched/Kconfig"
}Results: {
"Output": "net/sched/Kconfig=266=config NET_SCH_CODEL\n--\nnet/sched/Kconfig-276-\nnet/sched/Kconfig:277:config NET_SCH_FQ_CODEL\nnet/sched/Kconfig-278-\ttristate \"Fair Queue Controlled Delay AQM (FQ_CODEL)\"\n--\nnet/sched/Kconfig=288=config NET_SCH_CAKE\n--\nnet/sched/Kconfig-298-\nnet/sched/Kconfig:299:config NET_SCH_FQ\nnet/sched/Kconfig-300-\ttristate \"Fair Queue\"\n--\nnet/sched/Kconfig=322=config NET_SCH_PIE\n--\nnet/sched/Kconfig-333-\nnet/sched/Kconfig:334:config NET_SCH_FQ_PIE\nnet/sched/Kconfig-335-\tdepends on NET_SCH_PIE\n--\nnet/sched/Kconfig=443=choice\n--\nnet/sched/Kconfig-450-\tconfig DEFAULT_FQ\nnet/sched/Kconfig:451:\t\tbool \"Fair Queue\" if NET_SCH_FQ\nnet/sched/Kconfig-452-\n--\nnet/sched/Kconfig-456-\tconfig DEFAULT_FQ_CODEL\nnet/sched/Kconfig:457:\t\tbool \"Fair Queue Controlled Delay\" if NET_SCH_FQ_CODEL\nnet/sched/Kconfig-458-\nnet/sched/Kconfig-459-\tconfig DEFAULT_FQ_PIE\nnet/sched/Kconfig:460:\t\tbool \"Flow Queue Proportional Integral controller Enhanced\" if NET_SCH_FQ_PIE\nnet/sched/Kconfig-461-\n"
} |
| 5/2 | 2026/07/27 07:29 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20061 output: 31 thoughts: 33 Thoughts: Here's my take on those thoughts: **Analyzing Network Scheduler Configuration** Alright, so `NET_SCH_FQ` is definitely the configuration option that drives the functionality in `sch_fq.c`. I need to make sure I understand how it's being used and how it interacts with other components, especially given the packet generation aspect. Now, to get a complete picture, let's pivot to the packet generator's configuration – that's where I need to look at `pktgen.c` to see how everything is orchestrated. |
| 6/2 | 2026/07/27 07:29 | tool | grepper |
0mError:no matches Args: {
"Expression": "NET_PKTGEN",
"PathPrefix": "net/core/Kconfig"
}Results: {
"error": "no matches"
} |
| 7/2 | 2026/07/27 07:29 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20254 output: 27 thoughts: 36 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Debugging Packet Generation Module Placement** Alright, so I'm trying to nail down the precise location of `pktgen`. My immediate hunch is that it's buried somewhere in the `net/core/` directory, and the hint is that it's `pktgen.c` which often implies it is core, but I need to confirm that suspicion. That's a classic location for core network components. To do that, I'm going to quickly check either `net/Kconfig` to see if there's a configuration option related to `pktgen`, or I'll look into `net/core/Makefile` for the build directives and dependency information. Hopefully one of these files will explicitly state where the `pktgen` module belongs. From there, I can build an image to isolate the functionality. |
| 8/2 | 2026/07/27 07:29 | tool | grepper |
0mArgs:{
"Expression": "pktgen",
"PathPrefix": "net/core/Makefile"
}Results: {
"Output": "net/core/Makefile=26=obj-$(CONFIG_PROC_FS) += net-procfs.o\nnet/core/Makefile:27:obj-$(CONFIG_NET_PKTGEN) += pktgen.o\nnet/core/Makefile-28-obj-$(CONFIG_NETPOLL) += netpoll.o\n"
} |
| 9/2 | 2026/07/27 07:29 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20594 output: 20 thoughts: 11 Thoughts: Okay, here's my summary, presented as a stream of thought: **Finding the Definition of NET_PKTGEN** Alright, so I need to track down the definition of `NET_PKTGEN`. It's a key piece of this puzzle, and without knowing where it's declared, I'm essentially flying blind. Is it a macro? A function? A struct? Part of a broader library? The name feels like it could be related to network packet generation, which is a fairly common area, so that's a good starting point for a search. I need to figure out the scope of its use to determine where this is defined - is this a project specific definition or an already integrated piece of code? This will determine how I go about finding it, and may change the approach. I could just grep through the code base and see if it's there. Let's see... I'll start with a broader search, maybe in the include files, or possibly I'll narrow my search to a subfolder that is more relevant. Time to get to work! |
| 10/2 | 2026/07/27 07:29 | tool | grepper |
0mArgs:{
"Expression": "config NET_PKTGEN"
}Results: {
"Output": "net/Kconfig=384=menu \"Network testing\"\nnet/Kconfig-385-\nnet/Kconfig:386:config NET_PKTGEN\nnet/Kconfig-387-\ttristate \"Packet Generator (USE WITH CAUTION)\"\n"
} |
| 11/2 | 2026/07/27 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20929 output: 30 thoughts: 60 Thoughts: Here's my thought process, as if I were working through this: **Analyzing the Network Packet Generator Configuration** Okay, so I see `NET_PKTGEN` listed. My immediate thought is to trace how this impacts the system. Looks like I need to dig into the `net/ethtool/rings.c` file. I recall that `ethtool` interactions are sometimes conditioned by kernel configuration options. Let's see if the compilation of that specific file depends on a particular configuration flag. Bingo! It looks like `rings.c` is compiled when `CONFIG_ETHTOOL_NETLINK` is enabled. Now I should check the `net/ethtool/Makefile` to confirm the compilation dependencies and understand how this whole thing hangs together, particularly with that NETLINK dependency. I need to understand the relationship between packet generation, ethtool, and this netlink configuration. |
| 12/2 | 2026/07/27 07:30 | tool | grepper |
0mArgs:{
"Expression": "rings\\.o",
"PathPrefix": "net/ethtool/Makefile"
}Results: {
"Output": "net/ethtool/Makefile=7=ethtool_nl-y\t:= netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \\\nnet/ethtool/Makefile:8:\t\t linkstate.o debug.o wol.o features.o privflags.o rings.o \\\nnet/ethtool/Makefile-9-\t\t channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \\\n"
} |
| 13/2 | 2026/07/27 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 21332 output: 32 thoughts: 14 Thoughts: Okay, here's my summary, presented as a stream of thought: **Diving into the `ethtool_nl-y` Compilation** Alright, so *`ethtool_nl-y`*... that's the key file, isn't it? The one that determines which modules get compiled into the kernel. The whole question boils down to *when* exactly does this specific file get compiled? I need to understand the timing of that compilation. Knowing *when* will illuminate the dependencies, build order, and potentially, any implicit conditional logic in the makefiles or build system that controls it. That's the core of what I need to figure out. I suspect the answer is linked to the build system's decision tree, but I can't quite see the trigger for that compilation yet. So, I need to trace this back. When is `ethtool_nl-y` compiled? That's the question. |
| 14/2 | 2026/07/27 07:30 | tool | grepper |
0mArgs:{
"Expression": "ethtool_nl",
"PathPrefix": "net/ethtool/Makefile"
}Results: {
"Output": "net/ethtool/Makefile=3=obj-y\t\t\t\t+= ioctl.o common.o\nnet/ethtool/Makefile-4-\nnet/ethtool/Makefile:5:obj-$(CONFIG_ETHTOOL_NETLINK)\t+= ethtool_nl.o\nnet/ethtool/Makefile-6-\nnet/ethtool/Makefile:7:ethtool_nl-y\t:= netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \\\nnet/ethtool/Makefile-8-\t\t linkstate.o debug.o wol.o features.o privflags.o rings.o \\\n"
} |
| 15/2 | 2026/07/27 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 21745 output: 21 thoughts: 43 Thoughts: Okay, here's my take on those thoughts, framed as my own reasoning: **Checking for ETHTOOL_NETLINK Configuration** Right, so I see we're dealing with `ETHTOOL_NETLINK` here. My first step is to confirm where that's defined in the kernel configuration. I need to figure out if it's explicitly enabled or configurable. Based on my experience, it's most likely to be lurking in either `net/ethtool/Kconfig` or, perhaps more broadly, `net/Kconfig`. I'll start by checking those files directly. It's a quick scan, looking for that specific symbol to get a handle on its dependencies and default settings. Knowing the Kconfig options is essential for understanding how this feature is controlled within the kernel build. |
| 16/2 | 2026/07/27 07:30 | tool | grepper |
0mArgs:{
"Expression": "config ETHTOOL_NETLINK"
}Results: {
"Output": "net/Kconfig=507=config FAILOVER\n--\nnet/Kconfig-519-\nnet/Kconfig:520:config ETHTOOL_NETLINK\nnet/Kconfig-521-\tbool \"Netlink interface for ethtool\"\n"
} |
| 17/2 | 2026/07/27 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 22054 output: 32 thoughts: 260 Thoughts: **My Initial Assessment of the Key Functions** Okay, here's what I've got. The relevant configurations are `NET_SCH_FQ`, `NET_PKTGEN`, and `ETHTOOL_NETLINK`. I need to zero in on the core functions affected by these configs. Looking at the code, in `net/sched/sch_fq.c`, I'm seeing `fq_change`, `fq_dequeue`, and `fq_classify` as the primary functions of interest. Those are the traffic shaping bits. In `net/core/pktgen.c`, I have `pktgen_if_write` and `pktgen_xmit`, which are involved in packet generation. And in `net/ethtool/rings.c`, I see `ethnl_set_rings` and `ethnl_set_rings_validate`, which deals with network ring configuration through netlink. Then I see some functions in a driver. Hold on... `idpf` is an Intel Infrastructure Data Plane Function driver. Now, is that even relevant? Probably not directly, because it is a hardware-specific driver, so probably not relevant in virtualized environments. But the underlying networking changes are the important parts. So I think I should probably focus on those core networking functions. Now, just to be sure, let's take another look at that `fq_change` function in `net/sched/sch_fq.c`. |
| 18/2 | 2026/07/27 07:30 | tool | grepper |
0mArgs:{
"Expression": "fq_change",
"PathPrefix": "net/sched/sch_fq.c"
}Results: {
"Output": "net/sched/sch_fq.c=1058=static int fq_load_priomap(struct fq_sched_data *q,\n--\nnet/sched/sch_fq.c-1079-\nnet/sched/sch_fq.c:1080:static int fq_change(struct Qdisc *sch, struct nlattr *opt,\nnet/sched/sch_fq.c-1081-\t\t struct netlink_ext_ack *extack)\n--\nnet/sched/sch_fq.c=1233=static int fq_init(struct Qdisc *sch, struct nlattr *opt,\n--\nnet/sched/sch_fq.c-1271-\tif (opt)\nnet/sched/sch_fq.c:1272:\t\terr = fq_change(sch, opt, extack);\nnet/sched/sch_fq.c-1273-\telse\n--\nnet/sched/sch_fq.c=1389=static struct Qdisc_ops fq_qdisc_ops __read_mostly = {\n--\nnet/sched/sch_fq.c-1398-\t.destroy\t=\tfq_destroy,\nnet/sched/sch_fq.c:1399:\t.change\t\t=\tfq_change,\nnet/sched/sch_fq.c-1400-\t.dump\t\t=\tfq_dump,\n"
} |
| 19/2 | 2026/07/27 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 23010 output: 32 thoughts: 33 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Diving into the FQ Logic** Right, `fq_change` is where it all begins, the initial setup for the fair queuing discipline. That's the logical starting point, the entry to all the action. Now, the real meat of it is going to be in how it actually *functions*. I need to get down into the weeds and see how the packets are being handled, how traffic is actually managed once it's in the queue. My focus now is definitely on `fq_dequeue` and `fq_classify` – those are the functions I really want to understand. Let's see how the queued packets are selected and which ones get a priority bump or a shunting. |
| 20/2 | 2026/07/27 07:30 | tool | grepper |
0mArgs:{
"Expression": "fq_dequeue",
"PathPrefix": "net/sched/sch_fq.c"
}Results: {
"Output": "net/sched/sch_fq.c=69=struct fq_flow {\nnet/sched/sch_fq.c:70:/* First cache line : used in fq_gc(), fq_enqueue(), fq_dequeue() */\nnet/sched/sch_fq.c-71-\tstruct rb_root\tt_root;\n--\nnet/sched/sch_fq.c=111=struct fq_sched_data {\n--\nnet/sched/sch_fq.c-132-\nnet/sched/sch_fq.c:133:\tunsigned int band_nr; /* band being serviced in fq_dequeue() */\nnet/sched/sch_fq.c-134-\n--\nnet/sched/sch_fq.c=486=static void fq_erase_head(struct Qdisc *sch, struct fq_flow *flow,\n--\nnet/sched/sch_fq.c-502- */\nnet/sched/sch_fq.c:503:static void fq_dequeue_skb(struct Qdisc *sch, struct fq_flow *flow,\nnet/sched/sch_fq.c-504-\t\t\t struct sk_buff *skb)\n--\nnet/sched/sch_fq.c=699=static struct fq_flow_head *fq_pband_head_select(struct fq_perband_flows *pband)\n--\nnet/sched/sch_fq.c-709-\nnet/sched/sch_fq.c:710:static struct sk_buff *fq_dequeue(struct Qdisc *sch)\nnet/sched/sch_fq.c-711-{\n--\nnet/sched/sch_fq.c-728-\t\tq-\u003einternal.qlen--;\nnet/sched/sch_fq.c:729:\t\tfq_dequeue_skb(sch, \u0026q-\u003einternal, skb);\nnet/sched/sch_fq.c-730-\t\treturn skb;\n--\nnet/sched/sch_fq.c-776-\t\tprefetch(\u0026skb-\u003eend);\nnet/sched/sch_fq.c:777:\t\tfq_dequeue_skb(sch, f, skb);\nnet/sched/sch_fq.c-778-\t\tif (unlikely((s64)(now - time_next_packet - q-\u003ece_threshold) \u003e 0)) {\n--\nnet/sched/sch_fq.c=1389=static struct Qdisc_ops fq_qdisc_ops __read_mostly = {\n--\nnet/sched/sch_fq.c-1393-\t.enqueue\t=\tfq_enqueue,\nnet/sched/sch_fq.c:1394:\t.dequeue\t=\tfq_dequeue,\nnet/sched/sch_fq.c-1395-\t.peek\t\t=\tqdisc_peek_dequeued,\n"
} |
| 21/2 | 2026/07/27 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 23955 output: 35 thoughts: 33 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Analyzing Network Interface Ring Configuration** Alright, `fq_dequeue` is proving to be a solid solution in this context. My primary interest now is understanding how the underlying hardware rings are actually configured, specifically within the ethtool framework. Let's delve into `net/ethtool/rings.c` and take a closer look at the implementation of `ethnl_set_rings`. I need to see exactly how these ring settings, likely related to receive and transmit queues and their associated settings, are being translated into low-level device configuration. I need to understand what's happening under the hood - are we directly manipulating registers, using specific driver-defined calls, or interacting with a more generic interface? I need to analyze how we're handling things like ring size, interrupt moderation, and potentially even RSS parameters. This should inform any potential tuning decisions. |
| 22/2 | 2026/07/27 07:30 | tool | grepper |
0mArgs:{
"Expression": "ethnl_set_rings",
"PathPrefix": "net/ethtool/rings.c"
}Results: {
"Output": "net/ethtool/rings.c=161=static int\nnet/ethtool/rings.c:162:ethnl_set_rings_validate(struct ethnl_req_info *req_info,\nnet/ethtool/rings.c-163-\t\t\t struct genl_info *info)\n--\nnet/ethtool/rings.c=235=static int\nnet/ethtool/rings.c:236:ethnl_set_rings(struct ethnl_req_info *req_info, struct genl_info *info)\nnet/ethtool/rings.c-237-{\n--\nnet/ethtool/rings.c=337=const struct ethnl_request_ops ethnl_rings_request_ops = {\n--\nnet/ethtool/rings.c-347-\nnet/ethtool/rings.c:348:\t.set_validate\t\t= ethnl_set_rings_validate,\nnet/ethtool/rings.c:349:\t.set\t\t\t= ethnl_set_rings,\nnet/ethtool/rings.c-350-\t.set_ntf_cmd\t\t= ETHTOOL_MSG_RINGS_NTF,\n"
} |
| 23/2 | 2026/07/27 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 24532 output: 34 thoughts: 35 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Analyzing Packet Generation Functions** Alright, `ethnl_set_rings` seems like a solid piece of code, no immediate red flags there. Now, I want to drill down further into the packet generation process. My immediate thought is to check `pktgen_if_write` within `net/core/pktgen.c`. I need to understand how the user-provided data, and any configuration, is actually translated into a wire image. This function is likely where the final formatting, checksum calculations (if enabled), and insertion into the transmit queue are happening. Understanding the interaction between `pktgen_if_write` and lower-level networking components is crucial for optimizing packet injection performance and ensuring accurate payload transmission. I'll need to pay particular attention to potential bottlenecks, data copies, and any locking that might be involved. My primary focus is understanding the data path for packet generation. |
| 24/2 | 2026/07/27 07:30 | tool | grepper |
0mArgs:{
"Expression": "pktgen_if_write",
"PathPrefix": "net/core/pktgen.c"
}Results: {
"Output": "net/core/pktgen.c=971=static __u32 pktgen_read_flag(const char *f, bool *disable)\n--\nnet/core/pktgen.c-999-\nnet/core/pktgen.c:1000:static ssize_t pktgen_if_write(struct file *file,\nnet/core/pktgen.c-1001-\t\t\t const char __user *user_buffer, size_t count,\n--\nnet/core/pktgen.c=1939=static const struct proc_ops pktgen_if_proc_ops = {\n--\nnet/core/pktgen.c-1942-\t.proc_lseek\t= seq_lseek,\nnet/core/pktgen.c:1943:\t.proc_write\t= pktgen_if_write,\nnet/core/pktgen.c-1944-\t.proc_release\t= single_release,\n"
} |
| 25/2 | 2026/07/27 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25046 output: 33 thoughts: 34 Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person: **Analyzing the Packet Generation Workflow** Alright, so `pktgen_if_write` is looking solid. That seems to be handling the initial interface write as expected. Now, I want to dig a bit deeper into the transmission process itself. My next step is to examine `pktgen_xmit`, specifically within `net/core/pktgen.c`. I'm interested in understanding precisely how the generated packets are actually sent out on the wire from the core, given that initial check. I need to understand this portion of the code to ensure the path is consistent with the desired operation, and fully understand the transmission aspects. |
| 26/2 | 2026/07/27 07:30 | tool | grepper |
0mArgs:{
"Expression": "pktgen_xmit",
"PathPrefix": "net/core/pktgen.c"
}Results: {
"Output": "net/core/pktgen.c=1000=static ssize_t pktgen_if_write(struct file *file,\n--\nnet/core/pktgen.c-1406-\t\t\t/* make sure new packet is allocated every time\nnet/core/pktgen.c:1407:\t\t\t * pktgen_xmit() is called\nnet/core/pktgen.c-1408-\t\t\t */\n--\nnet/core/pktgen.c=3572=static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)\n--\nnet/core/pktgen.c-3587-\nnet/core/pktgen.c:3588:static void pktgen_xmit(struct pktgen_dev *pkt_dev)\nnet/core/pktgen.c-3589-{\n--\nnet/core/pktgen.c-3601-\t * prevent some concurrent changes from slipping in. And the stabilized\nnet/core/pktgen.c:3602:\t * config will be read in during the next run of pktgen_xmit.\nnet/core/pktgen.c-3603-\t */\n--\nnet/core/pktgen.c=3790=static int pktgen_thread_worker(void *arg)\n--\nnet/core/pktgen.c-3816-\t\tif (likely(pkt_dev)) {\nnet/core/pktgen.c:3817:\t\t\tpktgen_xmit(pkt_dev);\nnet/core/pktgen.c-3818-\n"
} |
| 27/2 | 2026/07/27 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25661 output: 32 thoughts: 76 Thoughts: Okay, here's my quick rundown of these functions, reflecting my initial assessment: **Functionality Overview** Alright, so `pktgen_xmit`, that's a key function, and seems to be in a good state. That's a good starting point. Now, to understand the surrounding ecosystem, the focus functions seem to be: `fq_change`, `fq_dequeue`, `ethnl_set_rings`, `pktgen_if_write`, and of course `pktgen_xmit`. Need to keep these functions in mind to establish the big picture. Looks like `fq_classify` has also seen some modifications; going to make a note of this to understand the extent of its influence. It's important to be thorough. Now, let's see if there are other, related functions in play that I might have missed. Gotta be meticulous. |
| 28/2 | 2026/07/27 07:30 | tool | grepper |
0mArgs:{
"Expression": "fq_classify",
"PathPrefix": "net/sched/sch_fq.c"
}Results: {
"Output": "net/sched/sch_fq.c=314=static bool fq_fastpath_check(const struct Qdisc *sch, struct sk_buff *skb,\n--\nnet/sched/sch_fq.c-355-\nnet/sched/sch_fq.c:356:static struct fq_flow *fq_classify(struct Qdisc *sch, struct sk_buff *skb,\nnet/sched/sch_fq.c-357-\t\t\t\t u64 now)\n--\nnet/sched/sch_fq.c=599=static int fq_enqueue(struct sk_buff *skb, struct Qdisc *sch,\n--\nnet/sched/sch_fq.c-632-\nnet/sched/sch_fq.c:633:\tf = fq_classify(sch, skb, now);\nnet/sched/sch_fq.c-634-\n"
} |
| 29/2 | 2026/07/27 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 26184 output: 36 thoughts: 34 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Analyzing Network Interface Information Handling** Alright, `fq_classify` is definitely a solid piece of the puzzle, seems well implemented from what I remember. But, let's drill down deeper, and to do that I need to look at how interface information is actually filled, and that means I need to go spelunking in the kernel. Let's see... *rtnl_fill_ifinfo* seems like the likely candidate for where that information is constructed. I'll need to check the netlink code directly in `net/core/rtnetlink.c` to confirm my suspicions and verify its specific behavior. I bet I can gain some new insights into the interface metadata population within that module. |
| 30/2 | 2026/07/27 07:31 | tool | grepper |
0mArgs:{
"Expression": "rtnl_fill_ifinfo",
"PathPrefix": "net/core/rtnetlink.c"
}Results: {
"Output": "net/core/rtnetlink.c=2068=static int rtnl_fill_dpll_pin(struct sk_buff *skb,\n--\nnet/core/rtnetlink.c-2089-\nnet/core/rtnetlink.c:2090:static int rtnl_fill_ifinfo(struct sk_buff *skb,\nnet/core/rtnetlink.c-2091-\t\t\t struct net_device *dev, struct net *src_net,\n--\nnet/core/rtnetlink.c=2524=static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)\n--\nnet/core/rtnetlink.c-2599-\t\t\tcontinue;\nnet/core/rtnetlink.c:2600:\t\terr = rtnl_fill_ifinfo(skb, dev, net, RTM_NEWLINK,\nnet/core/rtnetlink.c-2601-\t\t\t\t NETLINK_CB(cb-\u003eskb).portid,\n--\nnet/core/rtnetlink.c=4256=static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,\n--\nnet/core/rtnetlink.c-4328-\tif (nskb)\nnet/core/rtnetlink.c:4329:\t\terr = rtnl_fill_ifinfo(nskb, dev, net,\nnet/core/rtnetlink.c-4330-\t\t\t\t RTM_NEWLINK, NETLINK_CB(skb).portid,\n--\nnet/core/rtnetlink.c=4549=struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,\n--\nnet/core/rtnetlink.c-4568-\nnet/core/rtnetlink.c:4569:\terr = rtnl_fill_ifinfo(skb, dev, dev_net(dev),\nnet/core/rtnetlink.c-4570-\t\t\t type, portid, seq, change, 0, 0, event,\n"
} |
| 31/2 | 2026/07/27 07:31 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 26960 output: 39 thoughts: 73 Thoughts: Here's my summary, as the expert: **Assessment of Netlink and Ethtool Functionality for Fuzzing** Okay, so `rtnl_fill_ifinfo` is a pretty broad function, and it's used quite a bit in netlink dumps. While it works, it's a bit of a hot spot in the code, which could lead to some skewed results if I'm fuzzing with it. I'll make a mental note to skip it for now. I want to try to keep the fuzzing balanced across the functionality of the system. Now, let's turn our attention to `ethtool_ringparam_get_cfg` inside `net/ethtool/common.c`. I need to review this for some potential fuzzing targets. |
| 32/2 | 2026/07/27 07:31 | tool | grepper |
0mArgs:{
"Expression": "ethtool_ringparam_get_cfg",
"PathPrefix": "net/ethtool/common.c"
}Results: {
"Output": "net/ethtool/common.c=924=int ethtool_check_ops(const struct ethtool_ops *ops)\n--\nnet/ethtool/common.c-942-\nnet/ethtool/common.c:943:void ethtool_ringparam_get_cfg(struct net_device *dev,\nnet/ethtool/common.c-944-\t\t\t struct ethtool_ringparam *param,\n"
} |
| 33/2 | 2026/07/27 07:31 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 27364 output: 32 thoughts: 125 Thoughts: **Focusing on the Key Functions** Okay, so `ethtool_ringparam_get_cfg` is another get function, and frankly, not as intriguing as the set functions we're looking at. We're going to keep our attention firmly on these: `fq_change`, `fq_dequeue`, `fq_classify`, `ethnl_set_rings`, `pktgen_if_write`, and `pktgen_xmit`. Those seem to be where the real action is happening, specifically relating to configuration and manipulation of the system. Now, for the configurations themselves, we have `NET_SCH_FQ`, `NET_PKTGEN`, and `ETHTOOL_NETLINK`. I should double-check dependencies for `NET_SCH_FQ` though. We don't want to overlook any fundamental requirements there. Ensuring this configuration is independently usable will be important. |
| 34/2 | 2026/07/27 07:31 | tool | grepper |
0mArgs:{
"Expression": "config NET_SCH_FQ",
"PathPrefix": "net/sched/Kconfig"
}Results: {
"Output": "net/sched/Kconfig=266=config NET_SCH_CODEL\n--\nnet/sched/Kconfig-276-\nnet/sched/Kconfig:277:config NET_SCH_FQ_CODEL\nnet/sched/Kconfig-278-\ttristate \"Fair Queue Controlled Delay AQM (FQ_CODEL)\"\n--\nnet/sched/Kconfig=288=config NET_SCH_CAKE\n--\nnet/sched/Kconfig-298-\nnet/sched/Kconfig:299:config NET_SCH_FQ\nnet/sched/Kconfig-300-\ttristate \"Fair Queue\"\n--\nnet/sched/Kconfig=322=config NET_SCH_PIE\n--\nnet/sched/Kconfig-333-\nnet/sched/Kconfig:334:config NET_SCH_FQ_PIE\nnet/sched/Kconfig-335-\tdepends on NET_SCH_PIE\n"
} |
| 35/2 | 2026/07/27 07:31 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 27955 output: 30 thoughts: 27 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Quick Network Scheduler Assessment** Alright, let's see... `NET_SCH_FQ`. It doesn't look like that one's got any particularly complex dependencies I need to worry about. Seems straightforward on the surface, which is always nice. Now, just to be absolutely certain, let's take a quick look at `NET_PKTGEN`. I should probably verify how it interacts with the scheduler; there could be subtle interactions I'm missing at first glance, especially if it uses custom packet generation techniques. Better to be thorough, given the potential impact on performance and stability. |
| 36/2 | 2026/07/27 07:31 | tool | grepper |
0mArgs:{
"Expression": "config NET_PKTGEN",
"PathPrefix": "net/Kconfig"
}Results: {
"Output": "net/Kconfig=384=menu \"Network testing\"\nnet/Kconfig-385-\nnet/Kconfig:386:config NET_PKTGEN\nnet/Kconfig-387-\ttristate \"Packet Generator (USE WITH CAUTION)\"\n"
} |
| 37/2 | 2026/07/27 07:31 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28230 output: 31 thoughts: 34 Thoughts: Okay, here's my interpretation of those thoughts, presented as a personal thought process, as requested: **Assessing Dependencies in Network Packet Generation** Alright, so `NET_PKTGEN` is what we're working with here. Generally, it's pretty standard that `NET_PKTGEN` relies on `INET`, and that's usually the expected behavior. I'm not too concerned about that particular relationship. It makes sense, given the function. However, I'm feeling a quick itch to make sure everything is in order. Let's just double-check that `ETHTOOL_NETLINK` is set as expected, given the context. Just want to eliminate any potential gotchas. A quick sanity check never hurts, even when things *seem* fine. A little due diligence is always a good thing. |
| 38/2 | 2026/07/27 07:31 | tool | grepper |
0mArgs:{
"Expression": "config ETHTOOL_NETLINK",
"PathPrefix": "net/Kconfig"
}Results: {
"Output": "net/Kconfig=507=config FAILOVER\n--\nnet/Kconfig-519-\nnet/Kconfig:520:config ETHTOOL_NETLINK\nnet/Kconfig-521-\tbool \"Netlink interface for ethtool\"\n"
} |
| 39/2 | 2026/07/27 07:31 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28547 output: 202 thoughts: 418 Thoughts: **My Assessment of the Patch's Fuzzing Potential** Okay, let's break this down. First, this patch relies on `ETHTOOL_NETLINK`, which, of course, depends on both `NET` and `NETLINK`. I wonder if there are any other configuration dependencies lurking? Let me see. Alright, the code changes focus on `drivers/net/ethernet/intel/idpf/idpf_txrx.c`. Should I be concerned about an `IDPF` config requirement here? Based on the prompt, it sounds like this involves vendor-specific PCIe switch, SmartNIC, or GPU drivers, which usually expect actual hardware not emulated in QEMU. Thinking about it, `idpf` is Intel's Infrastructure Data Plane Function, a hardware driver. That makes sense. It's almost certainly not emulated in standard QEMU. So, I can disregard the `idpf` changes for fuzzing purposes because they likely won't be triggered in a standard QEMU environment. However, the key networking components that *are* changed - like `sch_fq.c`, `pktgen.c`, and the ethtool interface - these are reachable and functional even in a virtual environment. The core networking components are the heart of the matter. Therefore, `WorthFuzzing=true` is the right call here. Now, let's synthesize this. We need to focus on these symbols: * `fq_change` * `fq_dequeue` * `fq_classify` * `ethnl_set_rings` * `pktgen_if_write` * `pktgen_xmit` And we'll enable these configurations: * `NET_SCH_FQ` * `NET_PKTGEN` * `ETHTOOL_NETLINK` The patch introduces a new function for pacing offload horizon. It touches the Fair Queue packet scheduler (`sch_fq.c`), the packet generator (`pktgen.c`), and the ethtool netlink interface to enable this horizon. Given that these are core networking components that are accessible in virtualized environments, fuzzing is appropriate, even if the driver changes are hardware-specific. |
| 40/2 | 2026/07/27 07:31 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"NET_SCH_FQ",
"NET_PKTGEN",
"ETHTOOL_NETLINK"
],
"FocusSymbols": [
"fq_change",
"fq_dequeue",
"fq_classify",
"ethnl_set_rings",
"pktgen_if_write",
"pktgen_xmit"
],
"Reasoning": "The patch introduces a new feature for pacing offload horizon. It modifies the core Fair Queue packet scheduler (sch_fq.c), the packet generator (pktgen.c), and the ethtool netlink interface (rings.c) to support configuring and using this horizon. These core networking components are reachable and functional in virtualized environments, making them suitable for fuzzing. The changes to the idpf driver are hardware-specific and not emulated, but the core changes are sufficient to warrant fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"NET_SCH_FQ",
"NET_PKTGEN",
"ETHTOOL_NETLINK"
],
"FocusSymbols": [
"fq_change",
"fq_dequeue",
"fq_classify",
"ethnl_set_rings",
"pktgen_if_write",
"pktgen_xmit"
],
"Reasoning": "The patch introduces a new feature for pacing offload horizon. It modifies the core Fair Queue packet scheduler (sch_fq.c), the packet generator (pktgen.c), and the ethtool netlink interface (rings.c) to support configuring and using this horizon. These core networking components are reachable and functional in virtualized environments, making them suitable for fuzzing. The changes to the idpf driver are hardware-specific and not emulated, but the core changes are sufficient to warrant fuzzing.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|