| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/02 14:05 | flow | patch-triage |
5mArgs:null Results: {
"EnableConfigs": [
"RPS",
"SMP",
"SYSFS"
],
"FocusSymbols": [
"rps_llc_check",
"sock_rps_record_flow",
"sock_rps_record_flow_hash"
],
"KMSANReasoning": "The patch introduces an LLC-affinity-based optimization for Receive Packet Steering / Receive Flow Steering (RFS) flow table updates, adds a corresponding sysctl entry (rps_feat_llc_affinity) handled by proc_do_static_key, and out-of-lines/exports sock_rps_record_flow{,_hash}.\n\nAll operations in this patch work with scalar values (u32 hash/cpu IDs) and static keys. No new memory allocations, uninitialized stack or heap buffers, struct padding exposures, or kernel-to-user copy paths are introduced or modified. There is no risk of uninitialized memory reads or information leaks, so a dedicated KMSAN fuzzing session is not warranted; standard KASAN and kernel testing are sufficient.",
"NeedsKMSAN": false,
"Reasoning": "The patch introduces an LLC (Last Level Cache) affinity check for RPS/RFS flow updates controlled via a new sysctl (net.core.rps_feat_llc_affinity). It modifies core packet/socket receive steering logic in net/core/dev.c and moves sock_rps_record_flow/sock_rps_record_flow_hash to non-inline exported functions. This logic is fully reachable in standard virtualized environments.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/02 14:05 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit be9b9f94c3c4401362c8a44e77fd905493be8626\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Wed Sep 2 14:05:49 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/net/rps.h b/include/net/rps.h\nindex e33c6a2fa8bbc..6dacf0888a6c2 100644\n--- a/include/net/rps.h\n+++ b/include/net/rps.h\n@@ -12,6 +12,7 @@\n \n extern struct static_key_false rps_needed;\n extern struct static_key_false rfs_needed;\n+extern struct static_key_false rps_feat_llc_affinity;\n \n /*\n * This structure holds an RPS map which can be of variable length. The\n@@ -55,11 +56,14 @@ struct rps_sock_flow_table {\n \n #define RPS_NO_CPU 0xffff\n \n+bool rps_llc_check(u32 old_val, u32 new_val);\n+\n static inline void rps_record_sock_flow(rps_tag_ptr tag_ptr, u32 hash)\n {\n \tunsigned int index = hash \u0026 rps_tag_to_mask(tag_ptr);\n \tu32 val = hash \u0026 ~net_hotdata.rps_cpu_mask;\n \tstruct rps_sock_flow_table *table;\n+\tu32 old_val;\n \n \t/* We only give a hint, preemption can change CPU under us */\n \tval |= raw_smp_processor_id();\n@@ -68,7 +72,8 @@ static inline void rps_record_sock_flow(rps_tag_ptr tag_ptr, u32 hash)\n \t/* The following WRITE_ONCE() is paired with the READ_ONCE()\n \t * here, and another one in get_rps_cpu().\n \t */\n-\tif (READ_ONCE(table[index].ent) != val)\n+\told_val = READ_ONCE(table[index].ent);\n+\tif (old_val != val \u0026\u0026 rps_llc_check(old_val, val))\n \t\tWRITE_ONCE(table[index].ent, val);\n }\n \n@@ -136,25 +141,8 @@ static inline bool rfs_is_needed(void)\n #endif\n }\n \n-static inline void sock_rps_record_flow_hash(__u32 hash)\n-{\n-#ifdef CONFIG_RPS\n-\tif (!rfs_is_needed())\n-\t\treturn;\n-\n-\t_sock_rps_record_flow_hash(hash);\n-#endif\n-}\n-\n-static inline void sock_rps_record_flow(const struct sock *sk)\n-{\n-#ifdef CONFIG_RPS\n-\tif (!rfs_is_needed())\n-\t\treturn;\n-\n-\t_sock_rps_record_flow(sk);\n-#endif\n-}\n+void sock_rps_record_flow_hash(__u32 hash);\n+void sock_rps_record_flow(const struct sock *sk);\n \n static inline void sock_rps_delete_flow(const struct sock *sk)\n {\ndiff --git a/net/core/dev.c b/net/core/dev.c\nindex 38336858c168f..b27839eb594d6 100644\n--- a/net/core/dev.c\n+++ b/net/core/dev.c\n@@ -5052,6 +5052,8 @@ struct static_key_false rps_needed __read_mostly;\n EXPORT_SYMBOL(rps_needed);\n struct static_key_false rfs_needed __read_mostly;\n EXPORT_SYMBOL(rfs_needed);\n+struct static_key_false rps_feat_llc_affinity __read_mostly;\n+EXPORT_SYMBOL(rps_feat_llc_affinity);\n \n static u32 rfs_slot(u32 hash, rps_tag_ptr tag_ptr)\n {\n@@ -5263,6 +5265,48 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb,\n \treturn cpu;\n }\n \n+/**\n+ * rps_llc_check - determine if RPS flow table should be updated.\n+ * @old_val: previous flow record value.\n+ * @new_val: target flow record value.\n+ *\n+ * Return: true if the record needs an update, false otherwise.\n+ */\n+bool rps_llc_check(u32 old_val, u32 new_val)\n+{\n+\tu32 old_cpu = old_val \u0026 net_hotdata.rps_cpu_mask;\n+\tu32 new_cpu = new_val \u0026 net_hotdata.rps_cpu_mask;\n+\n+\t/*\n+\t * RPS LLC Affinity Feature:\n+\t * Reduce RFS/ARFS flow updates by checking LLC affinity.\n+\t *\n+\t * Frequent flow table updates can trigger constant hardware steering\n+\t * reconfigurations (e.g., ndo_rx_flow_steer), leading to significant\n+\t * contention on driver internal locks (like mlx5's arfs_lock).\n+\t *\n+\t * This strategy only updates the flow record if it migrates across LLC\n+\t * boundaries. This minimizes expensive hardware updates while preserving\n+\t * cache locality for the application.\n+\t */\n+\tif (static_branch_unlikely(\u0026rps_feat_llc_affinity)) {\n+\t\t/* Force update if the recorded CPU is invalid or has gone offline */\n+\t\tif (old_cpu \u003e= nr_cpu_ids || !cpu_active(old_cpu))\n+\t\t\treturn true;\n+\n+\t\t/*\n+\t\t * If CPUs do not share a cache, allow the update to prevent\n+\t\t * expensive remote memory accesses and cache misses.\n+\t\t */\n+\t\tif (!cpus_share_cache(old_cpu, new_cpu))\n+\t\t\treturn true;\n+\n+\t\treturn false;\n+\t}\n+\n+\treturn true;\n+}\n+\n #ifdef CONFIG_RFS_ACCEL\n \n /**\n@@ -5318,6 +5362,28 @@ static void rps_trigger_softirq(void *data)\n \n #endif /* CONFIG_RPS */\n \n+void sock_rps_record_flow_hash(__u32 hash)\n+{\n+#ifdef CONFIG_RPS\n+\tif (!rfs_is_needed())\n+\t\treturn;\n+\n+\t_sock_rps_record_flow_hash(hash);\n+#endif\n+}\n+EXPORT_SYMBOL(sock_rps_record_flow_hash);\n+\n+void sock_rps_record_flow(const struct sock *sk)\n+{\n+#ifdef CONFIG_RPS\n+\tif (!rfs_is_needed())\n+\t\treturn;\n+\n+\t_sock_rps_record_flow(sk);\n+#endif\n+}\n+EXPORT_SYMBOL(sock_rps_record_flow);\n+\n /* Called from hardirq (IPI) context */\n static void trigger_rx_softirq(void *data)\n {\ndiff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c\nindex eb35da3556f4a..9b6702f5eab19 100644\n--- a/net/core/sysctl_net_core.c\n+++ b/net/core/sysctl_net_core.c\n@@ -210,6 +210,7 @@ static int rps_sock_flow_sysctl(const struct ctl_table *table, int write,\n \tkvfree_rcu_mightsleep(tofree);\n \treturn ret;\n }\n+\n #endif /* CONFIG_RPS */\n \n #ifdef CONFIG_NET_FLOW_LIMIT\n@@ -554,6 +555,13 @@ static struct ctl_table net_core_table[] = {\n \t\t.mode\t\t= 0644,\n \t\t.proc_handler\t= rps_sock_flow_sysctl\n \t},\n+\t{\n+\t\t.procname\t= \"rps_feat_llc_affinity\",\n+\t\t.data\t\t= \u0026rps_feat_llc_affinity.key,\n+\t\t.maxlen\t\t= sizeof(rps_feat_llc_affinity.key),\n+\t\t.mode\t\t= 0644,\n+\t\t.proc_handler\t= proc_do_static_key\n+\t},\n #endif\n #ifdef CONFIG_NET_FLOW_LIMIT\n \t{\n"
} |
| 2/1 | 2026/09/02 14:05 | agent | patch-evaluator |
3mModel:core Args: null Results: {
"EnableConfigs": [
"RPS",
"SMP",
"SYSFS"
],
"FocusSymbols": [
"rps_llc_check",
"sock_rps_record_flow",
"sock_rps_record_flow_hash"
],
"Reasoning": "The patch introduces an LLC (Last Level Cache) affinity check for RPS/RFS flow updates controlled via a new sysctl (net.core.rps_feat_llc_affinity). It modifies core packet/socket receive steering logic in net/core/dev.c and moves sock_rps_record_flow/sock_rps_record_flow_hash to non-inline exported functions. This logic is fully reachable in standard virtualized environments.",
"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 be9b9f94c3c4401362c8a44e77fd905493be8626
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Sep 2 14:05:49 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/net/rps.h b/include/net/rps.h
index e33c6a2fa8bbc..6dacf0888a6c2 100644
--- a/include/net/rps.h
+++ b/include/net/rps.h
@@ -12,6 +12,7 @@
extern struct static_key_false rps_needed;
extern struct static_key_false rfs_needed;
+extern struct static_key_false rps_feat_llc_affinity;
/*
* This structure holds an RPS map which can be of variable length. The
@@ -55,11 +56,14 @@ struct rps_sock_flow_table {
#define RPS_NO_CPU 0xffff
+bool rps_llc_check(u32 old_val, u32 new_val);
+
static inline void rps_record_sock_flow(rps_tag_ptr tag_ptr, u32 hash)
{
unsigned int index = hash & rps_tag_to_mask(tag_ptr);
u32 val = hash & ~net_hotdata.rps_cpu_mask;
struct rps_sock_flow_table *table;
+ u32 old_val;
/* We only give a hint, preemption can change CPU under us */
val |= raw_smp_processor_id();
@@ -68,7 +72,8 @@ static inline void rps_record_sock_flow(rps_tag_ptr tag_ptr, u32 hash)
/* The following WRITE_ONCE() is paired with the READ_ONCE()
* here, and another one in get_rps_cpu().
*/
- if (READ_ONCE(table[index].ent) != val)
+ old_val = READ_ONCE(table[index].ent);
+ if (old_val != val && rps_llc_check(old_val, val))
WRITE_ONCE(table[index].ent, val);
}
@@ -136,25 +141,8 @@ static inline bool rfs_is_needed(void)
#endif
}
-static inline void sock_rps_record_flow_hash(__u32 hash)
-{
-#ifdef CONFIG_RPS
- if (!rfs_is_needed())
- return;
-
- _sock_rps_record_flow_hash(hash);
-#endif
-}
-
-static inline void sock_rps_record_flow(const struct sock *sk)
-{
-#ifdef CONFIG_RPS
- if (!rfs_is_needed())
- return;
-
- _sock_rps_record_flow(sk);
-#endif
-}
+void sock_rps_record_flow_hash(__u32 hash);
+void sock_rps_record_flow(const struct sock *sk);
static inline void sock_rps_delete_flow(const struct sock *sk)
{
diff --git a/net/core/dev.c b/net/core/dev.c
index 38336858c168f..b27839eb594d6 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5052,6 +5052,8 @@ struct static_key_false rps_needed __read_mostly;
EXPORT_SYMBOL(rps_needed);
struct static_key_false rfs_needed __read_mostly;
EXPORT_SYMBOL(rfs_needed);
+struct static_key_false rps_feat_llc_affinity __read_mostly;
+EXPORT_SYMBOL(rps_feat_llc_affinity);
static u32 rfs_slot(u32 hash, rps_tag_ptr tag_ptr)
{
@@ -5263,6 +5265,48 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb,
return cpu;
}
+/**
+ * rps_llc_check - determine if RPS flow table should be updated.
+ * @old_val: previous flow record value.
+ * @new_val: target flow record value.
+ *
+ * Return: true if the record needs an update, false otherwise.
+ */
+bool rps_llc_check(u32 old_val, u32 new_val)
+{
+ u32 old_cpu = old_val & net_hotdata.rps_cpu_mask;
+ u32 new_cpu = new_val & net_hotdata.rps_cpu_mask;
+
+ /*
+ * RPS LLC Affinity Feature:
+ * Reduce RFS/ARFS flow updates by checking LLC affinity.
+ *
+ * Frequent flow table updates can trigger constant hardware steering
+ * reconfigurations (e.g., ndo_rx_flow_steer), leading to significant
+ * contention on driver internal locks (like mlx5's arfs_lock).
+ *
+ * This strategy only updates the flow record if it migrates across LLC
+ * boundaries. This minimizes expensive hardware updates while preserving
+ * cache locality for the application.
+ */
+ if (static_branch_unlikely(&rps_feat_llc_affinity)) {
+ /* Force update if the recorded CPU is invalid or has gone offline */
+ if (old_cpu >= nr_cpu_ids || !cpu_active(old_cpu))
+ return true;
+
+ /*
+ * If CPUs do not share a cache, allow the update to prevent
+ * expensive remote memory accesses and cache misses.
+ */
+ if (!cpus_share_cache(old_cpu, new_cpu))
+ return true;
+
+ return false;
+ }
+
+ return true;
+}
+
#ifdef CONFIG_RFS_ACCEL
/**
@@ -5318,6 +5362,28 @@ static void rps_trigger_softirq(void *data)
#endif /* CONFIG_RPS */
+void sock_rps_record_flow_hash(__u32 hash)
+{
+#ifdef CONFIG_RPS
+ if (!rfs_is_needed())
+ return;
+
+ _sock_rps_record_flow_hash(hash);
+#endif
+}
+EXPORT_SYMBOL(sock_rps_record_flow_hash);
+
+void sock_rps_record_flow(const struct sock *sk)
+{
+#ifdef CONFIG_RPS
+ if (!rfs_is_needed())
+ return;
+
+ _sock_rps_record_flow(sk);
+#endif
+}
+EXPORT_SYMBOL(sock_rps_record_flow);
+
/* Called from hardirq (IPI) context */
static void trigger_rx_softirq(void *data)
{
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index eb35da3556f4a..9b6702f5eab19 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -210,6 +210,7 @@ static int rps_sock_flow_sysctl(const struct ctl_table *table, int write,
kvfree_rcu_mightsleep(tofree);
return ret;
}
+
#endif /* CONFIG_RPS */
#ifdef CONFIG_NET_FLOW_LIMIT
@@ -554,6 +555,13 @@ static struct ctl_table net_core_table[] = {
.mode = 0644,
.proc_handler = rps_sock_flow_sysctl
},
+ {
+ .procname = "rps_feat_llc_affinity",
+ .data = &rps_feat_llc_affinity.key,
+ .maxlen = sizeof(rps_feat_llc_affinity.key),
+ .mode = 0644,
+ .proc_handler = proc_do_static_key
+ },
#endif
#ifdef CONFIG_NET_FLOW_LIMIT
{
|
| 3/2 | 2026/09/02 14:05 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 3943 output: 16 thoughts: 215 Thoughts: **Analyzing This Patch: A Deep Dive into RPS and LLC Affinity** Okay, let's break down this patch. My initial scan tells me we're dealing with some refinements to Receive Packet Steering (RPS), specifically concerning LLC (Last Level Cache) affinity. The file modifications immediately point us in the right direction: we're touching `rps.h`, `dev.c`, and `sysctl_net_core.c`. This is all classic RPS territory. So, the meat of it: we're introducing a new sysctl, `net.core.rps_feat_llc_affinity`, which is key. This likely allows us to dynamically control the `rps_feat_llc_affinity` static key, which is a significant addition, giving us fine-grained control over how RPS interacts with the CPU cache. The `rps_record_sock_flow` function now calls `rps_llc_check`, which is smart; it suggests that we are attempting to optimize cache locality during packet processing. I'll need to dig into the implementation of `rps_llc_check` in `dev.c` to see precisely what's happening. The movement of `sock_rps_record_flow_hash` and `sock_rps_record_flow` from inlines in `rps.h` to exported symbols in `dev.c` also catches my eye. This hints at some sort of internal restructuring, potentially for code reuse or more robust module integration. That's good practice. I'll need to see how the code is *using* these exported symbols to understand the full impact. Now, my next step is to examine the associated `CONFIG` options. This patch clearly affects RPS functionality. Time to dig through the `Kconfig` files and determine which configuration options are involved. This will help me understand the scenarios where this functionality will be enabled and how it interacts with other system settings. This could be critical for understanding dependencies and the implications of enabling or disabling this feature in different deployments. |
| 4/2 | 2026/09/02 14:06 | tool | grepper |
0mArgs:{
"Expression": "config RPS"
}Results: {
"Output": "net/Kconfig=293=config MAX_SKB_FRAGS\n--\nnet/Kconfig-304-\nnet/Kconfig:305:config RPS\nnet/Kconfig-306-\tbool \"Receive packet steering\"\n"
} |
| 5/2 | 2026/09/02 14:06 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 4671 output: 32 thoughts: 20 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person: **Investigating Kconfig Dependencies** Okay, so I'm diving into the `net/Kconfig` file, specifically around line 305. I need to understand the dependencies at that point in the configuration, presumably for a network-related feature or module. The Kconfig syntax is pretty straightforward, but a quick scan will help me refresh my memory of the surrounding context and quickly zero in on any relevant `depends on` statements or other constraints. Hopefully, this section provides the clues I need to debug or understand the configuration behavior. Let's get to it. |
| 6/2 | 2026/09/02 14:06 | tool | read-file |
0mArgs:{
"File": "net/Kconfig",
"FirstLine": 300,
"LineCount": 30
}Results: {
"Contents": " 300:\t\t legacy drivers.\n 301:\t\t This also increases memory overhead of small packets,\n 302:\t\t and in drivers using build_skb().\n 303:\t\t If unsure, say 17.\n 304:\t\n 305:\tconfig RPS\n 306:\t\tbool \"Receive packet steering\"\n 307:\t\tdepends on SMP \u0026\u0026 SYSFS\n 308:\t\tdefault y\n 309:\t\thelp\n 310:\t\t Software receive side packet steering (RPS) distributes the\n 311:\t\t load of received packet processing across multiple CPUs.\n 312:\t\n 313:\tconfig RFS_ACCEL\n 314:\t\tbool \"Hardware acceleration of RFS\"\n 315:\t\tdepends on RPS\n 316:\t\tselect CPU_RMAP\n 317:\t\tdefault y\n 318:\t\thelp\n 319:\t\t Allowing drivers for multiqueue hardware with flow filter tables to\n 320:\t\t accelerate RFS.\n 321:\t\n 322:\tconfig SOCK_RX_QUEUE_MAPPING\n 323:\t\tbool\n 324:\t\n 325:\tconfig XPS\n 326:\t\tbool\n 327:\t\tdepends on SMP\n 328:\t\tselect SOCK_RX_QUEUE_MAPPING\n 329:\t\tdefault y\n"
} |
| 7/2 | 2026/09/02 14:06 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 5233 output: 36 thoughts: 22 Thoughts: Here's my summary, as though I'm thinking through this process: **Diving into the RPS Logic** Okay, so I need to understand how Receive Packet Steering (RPS) is being handled, specifically the LLC (Logical Link Control) check. My instinct is to head straight to the core of the networking stack – `net/core/dev.c`. I expect to find the relevant code around `rps_llc_check`. This is where the lower-level decisions about where to steer incoming packets based on their LLC information should be made. Let's see what the function actually does and what its control flow is. I'll be looking for clues on how the RPS configuration interacts with the Ethernet header processing at this stage, and what the criteria are for directing packets to specific CPUs. I'm hoping to get a clearer picture of how traffic is being distributed and if there are any potential bottlenecks related to the LLC check itself. |
| 8/2 | 2026/09/02 14:07 | tool | read-file |
0mArgs:{
"File": "net/core/dev.c",
"FirstLine": 5260,
"LineCount": 60
}Results: {
"Contents": "5260:\t\t\t\tgoto done;\n5261:\t\t\t}\n5262:\t\t}\n5263:\t\n5264:\tdone:\n5265:\t\treturn cpu;\n5266:\t}\n5267:\t\n5268:\t/**\n5269:\t * rps_llc_check - determine if RPS flow table should be updated.\n5270:\t * @old_val: previous flow record value.\n5271:\t * @new_val: target flow record value.\n5272:\t *\n5273:\t * Return: true if the record needs an update, false otherwise.\n5274:\t */\n5275:\tbool rps_llc_check(u32 old_val, u32 new_val)\n5276:\t{\n5277:\t\tu32 old_cpu = old_val \u0026 net_hotdata.rps_cpu_mask;\n5278:\t\tu32 new_cpu = new_val \u0026 net_hotdata.rps_cpu_mask;\n5279:\t\n5280:\t\t/*\n5281:\t\t * RPS LLC Affinity Feature:\n5282:\t\t * Reduce RFS/ARFS flow updates by checking LLC affinity.\n5283:\t\t *\n5284:\t\t * Frequent flow table updates can trigger constant hardware steering\n5285:\t\t * reconfigurations (e.g., ndo_rx_flow_steer), leading to significant\n5286:\t\t * contention on driver internal locks (like mlx5's arfs_lock).\n5287:\t\t *\n5288:\t\t * This strategy only updates the flow record if it migrates across LLC\n5289:\t\t * boundaries. This minimizes expensive hardware updates while preserving\n5290:\t\t * cache locality for the application.\n5291:\t\t */\n5292:\t\tif (static_branch_unlikely(\u0026rps_feat_llc_affinity)) {\n5293:\t\t\t/* Force update if the recorded CPU is invalid or has gone offline */\n5294:\t\t\tif (old_cpu \u003e= nr_cpu_ids || !cpu_active(old_cpu))\n5295:\t\t\t\treturn true;\n5296:\t\n5297:\t\t\t/*\n5298:\t\t\t * If CPUs do not share a cache, allow the update to prevent\n5299:\t\t\t * expensive remote memory accesses and cache misses.\n5300:\t\t\t */\n5301:\t\t\tif (!cpus_share_cache(old_cpu, new_cpu))\n5302:\t\t\t\treturn true;\n5303:\t\n5304:\t\t\treturn false;\n5305:\t\t}\n5306:\t\n5307:\t\treturn true;\n5308:\t}\n5309:\t\n5310:\t#ifdef CONFIG_RFS_ACCEL\n5311:\t\n5312:\t/**\n5313:\t * rps_may_expire_flow - check whether an RFS hardware filter may be removed\n5314:\t * @dev: Device on which the filter was set\n5315:\t * @rxq_index: RX queue index\n5316:\t * @flow_id: Flow ID passed to ndo_rx_flow_steer()\n5317:\t * @filter_id: Filter ID returned by ndo_rx_flow_steer()\n5318:\t *\n5319:\t * Drivers that implement ndo_rx_flow_steer() should periodically call\n"
} |
| 9/2 | 2026/09/02 14:07 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 6380 output: 36 thoughts: 44 Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective: **Debugging RPS Configuration** Alright, so I'm trying to figure out if `rps_llc_check` is actually compiled into the kernel when `CONFIG_RPS` is enabled. It's a common oversight, and a subtle bug in our network stack can cause significant performance problems. The most likely place to start is the `net/core/dev.c` file, since that's where a lot of the core device driver functionality is implemented, especially for RPS and RFS logic. Given my experience, I'd expect to find a `#ifdef CONFIG_RPS` block in that area, and I'm betting that the function call, `rps_llc_check`, is enclosed within it. Let me check the code around lines 5040-5060 in `net/core/dev.c` to confirm. It's time to dig in and verify the preprocessor directives. |
| 10/2 | 2026/09/02 14:07 | tool | read-file |
0mArgs:{
"File": "net/core/dev.c",
"FirstLine": 5040,
"LineCount": 40
}Results: {
"Contents": "5040:\t\tlist_add_tail(\u0026napi-\u003epoll_list, \u0026sd-\u003epoll_list);\n5041:\t\tWRITE_ONCE(napi-\u003elist_owner, smp_processor_id());\n5042:\t\t/* If not called from net_rx_action()\n5043:\t\t * we have to raise NET_RX_SOFTIRQ.\n5044:\t\t */\n5045:\t\tif (!sd-\u003ein_net_rx_action)\n5046:\t\t\traise_softirq_irqoff(NET_RX_SOFTIRQ);\n5047:\t}\n5048:\t\n5049:\t#ifdef CONFIG_RPS\n5050:\t\n5051:\tstruct static_key_false rps_needed __read_mostly;\n5052:\tEXPORT_SYMBOL(rps_needed);\n5053:\tstruct static_key_false rfs_needed __read_mostly;\n5054:\tEXPORT_SYMBOL(rfs_needed);\n5055:\tstruct static_key_false rps_feat_llc_affinity __read_mostly;\n5056:\tEXPORT_SYMBOL(rps_feat_llc_affinity);\n5057:\t\n5058:\tstatic u32 rfs_slot(u32 hash, rps_tag_ptr tag_ptr)\n5059:\t{\n5060:\t\treturn hash_32(hash, rps_tag_to_log(tag_ptr));\n5061:\t}\n5062:\t\n5063:\t#ifdef CONFIG_RFS_ACCEL\n5064:\t/**\n5065:\t * rps_flow_is_active - check whether the flow is recently active.\n5066:\t * @rflow: Specific flow to check activity.\n5067:\t * @log: ilog2(hashsize).\n5068:\t * @cpu: CPU saved in @rflow.\n5069:\t *\n5070:\t * If the CPU has processed many packets since the flow's last activity\n5071:\t * (beyond 10 times the table size), the flow is considered stale.\n5072:\t *\n5073:\t * Return: true if flow was recently active.\n5074:\t */\n5075:\tstatic bool rps_flow_is_active(struct rps_dev_flow *rflow,\n5076:\t\t\t\t u8 log,\n5077:\t\t\t\t unsigned int cpu)\n5078:\t{\n5079:\t\tunsigned int flow_last_active;\n"
} |
| 11/2 | 2026/09/02 14:07 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 7342 output: 33 thoughts: 22 Thoughts: Here's my summary, as if I were thinking these thoughts: **RPS Disablement Deep Dive** Okay, so we're looking at RPS and how it behaves when the kernel is compiled without `CONFIG_RPS` enabled. My gut feeling is that even when RPS is off, there might still be some code paths or definitions that touch upon RPS-related structures or functions. We need to be thorough and verify that things are handled correctly. So, let's make sure we specifically check the `include/net/rps.h` header file even in this disabled configuration. It's a key place to look for those potential remnants or conditional compilations. Gotta be extra careful here. |
| 12/2 | 2026/09/02 14:07 | tool | read-file |
0mArgs:{
"File": "include/net/rps.h",
"FirstLine": 1,
"LineCount": 80
}Results: {
"Contents": " 1:\t/* SPDX-License-Identifier: GPL-2.0-or-later */\n 2:\t#ifndef _NET_RPS_H\n 3:\t#define _NET_RPS_H\n 4:\t\n 5:\t#include \u003clinux/types.h\u003e\n 6:\t#include \u003clinux/static_key.h\u003e\n 7:\t#include \u003cnet/sock.h\u003e\n 8:\t#include \u003cnet/hotdata.h\u003e\n 9:\t\n 10:\t#ifdef CONFIG_RPS\n 11:\t#include \u003cnet/rps-types.h\u003e\n 12:\t\n 13:\textern struct static_key_false rps_needed;\n 14:\textern struct static_key_false rfs_needed;\n 15:\textern struct static_key_false rps_feat_llc_affinity;\n 16:\t\n 17:\t/*\n 18:\t * This structure holds an RPS map which can be of variable length. The\n 19:\t * map is an array of CPUs.\n 20:\t */\n 21:\tstruct rps_map {\n 22:\t\tunsigned int\tlen;\n 23:\t\tstruct rcu_head\trcu;\n 24:\t\tu16\t\tcpus[];\n 25:\t};\n 26:\t#define RPS_MAP_SIZE(_num) (sizeof(struct rps_map) + ((_num) * sizeof(u16)))\n 27:\t\n 28:\t/*\n 29:\t * The rps_dev_flow structure contains the mapping of a flow to a CPU, the\n 30:\t * tail pointer for that CPU's input queue at the time of last enqueue, a\n 31:\t * hardware filter index, and the hash of the flow if aRFS is enabled.\n 32:\t */\n 33:\tstruct rps_dev_flow {\n 34:\t\tu16\t\tcpu;\n 35:\t\tu16\t\tfilter;\n 36:\t\tunsigned int\tlast_qtail;\n 37:\t#ifdef CONFIG_RFS_ACCEL\n 38:\t\tu32\t\thash;\n 39:\t#endif\n 40:\t};\n 41:\t#define RPS_NO_FILTER 0xffff\n 42:\t\n 43:\t/*\n 44:\t * The rps_sock_flow_table contains mappings of flows to the last CPU\n 45:\t * on which they were processed by the application (set in recvmsg).\n 46:\t * Each entry is a 32bit value. Upper part is the high-order bits\n 47:\t * of flow hash, lower part is CPU number.\n 48:\t * rps_cpu_mask is used to partition the space, depending on number of\n 49:\t * possible CPUs : rps_cpu_mask = roundup_pow_of_two(nr_cpu_ids) - 1\n 50:\t * For example, if 64 CPUs are possible, rps_cpu_mask = 0x3f,\n 51:\t * meaning we use 32-6=26 bits for the hash.\n 52:\t */\n 53:\tstruct rps_sock_flow_table {\n 54:\t\tu32\tent;\n 55:\t};\n 56:\t\n 57:\t#define RPS_NO_CPU 0xffff\n 58:\t\n 59:\tbool rps_llc_check(u32 old_val, u32 new_val);\n 60:\t\n 61:\tstatic inline void rps_record_sock_flow(rps_tag_ptr tag_ptr, u32 hash)\n 62:\t{\n 63:\t\tunsigned int index = hash \u0026 rps_tag_to_mask(tag_ptr);\n 64:\t\tu32 val = hash \u0026 ~net_hotdata.rps_cpu_mask;\n 65:\t\tstruct rps_sock_flow_table *table;\n 66:\t\tu32 old_val;\n 67:\t\n 68:\t\t/* We only give a hint, preemption can change CPU under us */\n 69:\t\tval |= raw_smp_processor_id();\n 70:\t\n 71:\t\ttable = rps_tag_to_table(tag_ptr);\n 72:\t\t/* The following WRITE_ONCE() is paired with the READ_ONCE()\n 73:\t\t * here, and another one in get_rps_cpu().\n 74:\t\t */\n 75:\t\told_val = READ_ONCE(table[index].ent);\n 76:\t\tif (old_val != val \u0026\u0026 rps_llc_check(old_val, val))\n 77:\t\t\tWRITE_ONCE(table[index].ent, val);\n 78:\t}\n 79:\t\n 80:\tstatic inline void _sock_rps_record_flow_hash(__u32 hash)\n"
} |
| 13/2 | 2026/09/02 14:07 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 8787 output: 34 thoughts: 18 Thoughts: Here's my take on the `include/net/rps.h` header, keeping in mind my deep dive into networking internals: **Diving into the `rps.h` Header** Alright, let's get into the nitty-gritty of `include/net/rps.h`. My immediate thought is, "This is where the magic happens, or at least, a significant part of it, in terms of steering packets to the right CPU core for processing." I'm expecting to see definitions related to Receive Packet Steering (RPS), and likely some data structures that are key to making this happen efficiently. I'm keen to see how the kernel handles the initial dispatch and what mechanisms it uses to determine the destination CPU, ideally, without too much overhead or locking contention. I'm hoping to refresh my understanding of how it integrates with other core networking components like the network driver and the NIC's own RSS (Receive Side Scaling) capabilities. I'm also looking for details on any configuration parameters that might be exposed to userspace, which would be interesting from a tuning and performance optimization perspective. |
| 14/2 | 2026/09/02 14:08 | tool | read-file |
0mArgs:{
"File": "include/net/rps.h",
"FirstLine": 81,
"LineCount": 80
}Results: {
"Contents": " 81:\t{\n 82:\t\trps_tag_ptr tag_ptr;\n 83:\t\n 84:\t\tif (!hash)\n 85:\t\t\treturn;\n 86:\t\trcu_read_lock();\n 87:\t\ttag_ptr = READ_ONCE(net_hotdata.rps_sock_flow_table);\n 88:\t\tif (tag_ptr)\n 89:\t\t\trps_record_sock_flow(tag_ptr, hash);\n 90:\t\trcu_read_unlock();\n 91:\t}\n 92:\t\n 93:\tstatic inline void _sock_rps_record_flow(const struct sock *sk)\n 94:\t{\n 95:\t\t/* Reading sk-\u003esk_rxhash might incur an expensive cache line\n 96:\t\t * miss.\n 97:\t\t *\n 98:\t\t * TCP_ESTABLISHED does cover almost all states where RFS\n 99:\t\t * might be useful, and is cheaper [1] than testing :\n 100:\t\t *\tIPv4: inet_sk(sk)-\u003einet_daddr\n 101:\t\t *\tIPv6: ipv6_addr_any(\u0026sk-\u003esk_v6_daddr)\n 102:\t\t * OR\tan additional socket flag\n 103:\t\t * [1] : sk_state and sk_prot are in the same cache line.\n 104:\t\t */\n 105:\t\tif (sk-\u003esk_state == TCP_ESTABLISHED) {\n 106:\t\t\t/* This READ_ONCE() is paired with the WRITE_ONCE()\n 107:\t\t\t * from sock_rps_save_rxhash() and sock_rps_reset_rxhash().\n 108:\t\t\t */\n 109:\t\t\t_sock_rps_record_flow_hash(READ_ONCE(sk-\u003esk_rxhash));\n 110:\t\t}\n 111:\t}\n 112:\t\n 113:\tstatic inline void _sock_rps_delete_flow(const struct sock *sk)\n 114:\t{\n 115:\t\tstruct rps_sock_flow_table *table;\n 116:\t\trps_tag_ptr tag_ptr;\n 117:\t\tu32 hash, index;\n 118:\t\n 119:\t\thash = READ_ONCE(sk-\u003esk_rxhash);\n 120:\t\tif (!hash)\n 121:\t\t\treturn;\n 122:\t\n 123:\t\trcu_read_lock();\n 124:\t\ttag_ptr = READ_ONCE(net_hotdata.rps_sock_flow_table);\n 125:\t\tif (tag_ptr) {\n 126:\t\t\tindex = hash \u0026 rps_tag_to_mask(tag_ptr);\n 127:\t\t\ttable = rps_tag_to_table(tag_ptr);\n 128:\t\t\tif (READ_ONCE(table[index].ent) != RPS_NO_CPU)\n 129:\t\t\t\tWRITE_ONCE(table[index].ent, RPS_NO_CPU);\n 130:\t\t}\n 131:\t\trcu_read_unlock();\n 132:\t}\n 133:\t#endif /* CONFIG_RPS */\n 134:\t\n 135:\tstatic inline bool rfs_is_needed(void)\n 136:\t{\n 137:\t#ifdef CONFIG_RPS\n 138:\t\treturn static_branch_unlikely(\u0026rfs_needed);\n 139:\t#else\n 140:\t\treturn false;\n 141:\t#endif\n 142:\t}\n 143:\t\n 144:\tvoid sock_rps_record_flow_hash(__u32 hash);\n 145:\tvoid sock_rps_record_flow(const struct sock *sk);\n 146:\t\n 147:\tstatic inline void sock_rps_delete_flow(const struct sock *sk)\n 148:\t{\n 149:\t#ifdef CONFIG_RPS\n 150:\t\tif (!rfs_is_needed())\n 151:\t\t\treturn;\n 152:\t\n 153:\t\t_sock_rps_delete_flow(sk);\n 154:\t#endif\n 155:\t}\n 156:\t\n 157:\tstatic inline u32 rps_input_queue_tail_incr(struct softnet_data *sd)\n 158:\t{\n 159:\t#ifdef CONFIG_RPS\n 160:\t\treturn ++sd-\u003einput_queue_tail;\n"
} |
| 15/2 | 2026/09/02 14:08 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 10253 output: 21 thoughts: 960 Thoughts: **Analyzing the Impact and Fuzzing Potential of This Patch** Okay, let's break this down. First, this patch touches the receive path, specifically when RPS/RFS are active and the `net.core.rps_feat_llc_affinity` sysctl is enabled. I need to understand what `rps_llc_check` is doing. Looking at the code, it seems to be involved in flow updates, CPU affinity, and steering within the RPS framework. Specifically, it's checking `old_cpu` and `new_cpu` against `net_hotdata.rps_cpu_mask`, and conditionally calling `cpu_active` and `cpus_share_cache` based on the `static_branch_unlikely` condition. Now, are these changes significant enough to warrant fuzzing? I need to apply the criteria. It's not documentation, a comment change, a logging update, or a purely cosmetic numeric change. There's core networking logic being modified in `net/core/dev.c`, `include/net/rps.h`, and `net/core/sysctl_net_core.c`. This isn't vendor-specific driver code or hot-unplug related code. Critically, these changes are reachable in a virtualized environment – a standard QEMU setup would be fine. Therefore, **WorthFuzzing=true**. Next, let's determine the `FocusSymbols`. Clearly, `rps_llc_check` is a key function to target. It's a non-static boolean function introducing new logic with the `static_branch_unlikely` and the `cpus_share_cache` check. This is new and warrants attention. Also, `sock_rps_record_flow` and `sock_rps_record_flow_hash`, which are now exported functions, are also good candidates for focused fuzzing. They are non-static and not inlined. Are they hot-path? I should be careful to only flag functions to fuzz that are not generic hot-path functions. This patch introduces this new `rps_llc_check` that checks cache affinity, so that seems like the primary function to hit. The other two functions are called from socket ops. I'll need to confirm exactly where `sock_rps_record_flow` is called to make sure that these are good targets, but for now they seem okay. |
| 16/2 | 2026/09/02 14:08 | tool | grepper |
0mArgs:{
"Expression": "sock_rps_record_flow"
}Results: {
"Output": "drivers/net/tun.c=414=static void tun_flow_update(struct tun_struct *tun, u32 rxhash,\n--\ndrivers/net/tun.c-432-\t\t\te-\u003eupdated = jiffies;\ndrivers/net/tun.c:433:\t\tsock_rps_record_flow_hash(e-\u003erps_rxhash);\ndrivers/net/tun.c-434-\t} else {\n--\ninclude/net/rps.h=61=static inline void rps_record_sock_flow(rps_tag_ptr tag_ptr, u32 hash)\n--\ninclude/net/rps.h-79-\ninclude/net/rps.h:80:static inline void _sock_rps_record_flow_hash(__u32 hash)\ninclude/net/rps.h-81-{\n--\ninclude/net/rps.h-92-\ninclude/net/rps.h:93:static inline void _sock_rps_record_flow(const struct sock *sk)\ninclude/net/rps.h-94-{\n--\ninclude/net/rps.h-108-\t\t */\ninclude/net/rps.h:109:\t\t_sock_rps_record_flow_hash(READ_ONCE(sk-\u003esk_rxhash));\ninclude/net/rps.h-110-\t}\n--\ninclude/net/rps.h=135=static inline bool rfs_is_needed(void)\n--\ninclude/net/rps.h-143-\ninclude/net/rps.h:144:void sock_rps_record_flow_hash(__u32 hash);\ninclude/net/rps.h:145:void sock_rps_record_flow(const struct sock *sk);\ninclude/net/rps.h-146-\n--\ninclude/net/sock.h=1205=static inline void sock_rps_save_rxhash(struct sock *sk,\n--\ninclude/net/sock.h-1209-\t/* The following WRITE_ONCE() is paired with the READ_ONCE()\ninclude/net/sock.h:1210:\t * here, and another one in sock_rps_record_flow().\ninclude/net/sock.h-1211-\t */\n--\ninclude/net/sock.h=1217=static inline void sock_rps_reset_rxhash(struct sock *sk)\n--\ninclude/net/sock.h-1219-#ifdef CONFIG_RPS\ninclude/net/sock.h:1220:\t/* Paired with READ_ONCE() in sock_rps_record_flow() */\ninclude/net/sock.h-1221-\tWRITE_ONCE(sk-\u003esk_rxhash, 0);\n--\nio_uring/zcrx.c=1953=int io_zcrx_recv(struct io_kiocb *req, struct io_zcrx_ifq *ifq,\n--\nio_uring/zcrx.c-1962-\nio_uring/zcrx.c:1963:\tsock_rps_record_flow(sk);\nio_uring/zcrx.c-1964-\treturn io_zcrx_tcp_recvmsg(req, ifq, sk, flags, issue_flags, len);\n--\nnet/core/dev.c=5354=static void rps_trigger_softirq(void *data)\n--\nnet/core/dev.c-5364-\nnet/core/dev.c:5365:void sock_rps_record_flow_hash(__u32 hash)\nnet/core/dev.c-5366-{\n--\nnet/core/dev.c-5370-\nnet/core/dev.c:5371:\t_sock_rps_record_flow_hash(hash);\nnet/core/dev.c-5372-#endif\nnet/core/dev.c-5373-}\nnet/core/dev.c:5374:EXPORT_SYMBOL(sock_rps_record_flow_hash);\nnet/core/dev.c-5375-\nnet/core/dev.c:5376:void sock_rps_record_flow(const struct sock *sk)\nnet/core/dev.c-5377-{\n--\nnet/core/dev.c-5381-\nnet/core/dev.c:5382:\t_sock_rps_record_flow(sk);\nnet/core/dev.c-5383-#endif\nnet/core/dev.c-5384-}\nnet/core/dev.c:5385:EXPORT_SYMBOL(sock_rps_record_flow);\nnet/core/dev.c-5386-\n--\nnet/ipv4/af_inet.c=761=void __inet_accept(struct socket *sock, struct socket *newsock, struct sock *newsk)\n--\nnet/ipv4/af_inet.c-767-\nnet/ipv4/af_inet.c:768:\tsock_rps_record_flow(newsk);\nnet/ipv4/af_inet.c-769-\tWARN_ON(!((1 \u003c\u003c newsk-\u003esk_state) \u0026\n--\nnet/ipv4/af_inet.c=844=int inet_send_prepare(struct sock *sk)\nnet/ipv4/af_inet.c-845-{\nnet/ipv4/af_inet.c:846:\tsock_rps_record_flow(sk);\nnet/ipv4/af_inet.c-847-\n--\nnet/ipv4/af_inet.c=884=int inet_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,\n--\nnet/ipv4/af_inet.c-890-\tif (likely(!(flags \u0026 MSG_ERRQUEUE)))\nnet/ipv4/af_inet.c:891:\t\tsock_rps_record_flow(sk);\nnet/ipv4/af_inet.c-892-\n--\nnet/ipv4/tcp.c=811=ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,\n--\nnet/ipv4/tcp.c-824-\nnet/ipv4/tcp.c:825:\tsock_rps_record_flow(sk);\nnet/ipv4/tcp.c-826-\t/*\n--\nnet/ipv4/tcp.c=2196=static int tcp_zerocopy_receive(struct sock *sk,\n--\nnet/ipv4/tcp.c-2223-\nnet/ipv4/tcp.c:2224:\tsock_rps_record_flow(sk);\nnet/ipv4/tcp.c-2225-\n--\nnet/ipv6/af_inet6.c=645=int inet6_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,\n--\nnet/ipv6/af_inet6.c-651-\tif (likely(!(flags \u0026 MSG_ERRQUEUE)))\nnet/ipv6/af_inet6.c:652:\t\tsock_rps_record_flow(sk);\nnet/ipv6/af_inet6.c-653-\n--\nnet/mptcp/protocol.c=1980=static void mptcp_rps_record_subflows(const struct mptcp_sock *msk)\n--\nnet/mptcp/protocol.c-1989-\nnet/mptcp/protocol.c:1990:\t\tsock_rps_record_flow(ssk);\nnet/mptcp/protocol.c-1991-\t}\n--\nnet/sctp/socket.c=8712=__poll_t sctp_poll(struct file *file, struct socket *sock, poll_table *wait)\n--\nnet/sctp/socket.c-8719-\nnet/sctp/socket.c:8720:\tsock_rps_record_flow(sk);\nnet/sctp/socket.c-8721-\n"
} |
| 17/2 | 2026/09/02 14:08 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 13763 output: 20 thoughts: 154 Thoughts: **My Analysis of the `rps_llc_check` Function's Dependencies** Okay, let's break this down. My initial focus is on the `rps_llc_check` function, seeing as it's the target. I know it's called from `rps_record_sock_flow` which is, in turn, called during the sock flow recording process. So, I need to understand its dependencies. My first thought is about Kconfig options. What configuration flags are going to be required for this functionality to be built and work correctly? I'll start by checking the obvious place: `net/Kconfig`. I see `CONFIG_RPS` defined there: ``` config RPS bool "Receive packet steering" depends on SMP && SYSFS default y ``` Alright, that's straightforward. We need `RPS` (the core feature itself), `SMP` (symmetric multiprocessing, which makes sense for packet steering across multiple CPUs), and `SYSFS` (for the sysfs interface). Seems logical. But, I'm a stickler for thoroughness, I am going to quickly double-check that `rps_llc_check` isn't indirectly dependent on anything else via other kernel functions or configurations. The best way to do this is to check the `cpus_share_cache` definition in the kernel tree and work back to determine how `rps_llc_check` is involved, if at all. |
| 18/2 | 2026/09/02 14:08 | tool | grepper |
0mArgs:{
"Expression": "cpus_share_cache"
}Results: {
"Output": "block/blk-mq.c=1241=static inline bool blk_mq_complete_need_ipi(struct request *rq)\n--\nblock/blk-mq.c-1259-\t (!test_bit(QUEUE_FLAG_SAME_FORCE, \u0026rq-\u003eq-\u003equeue_flags) \u0026\u0026\nblock/blk-mq.c:1260:\t cpus_share_cache(cpu, rq-\u003emq_ctx-\u003ecpu) \u0026\u0026\nblock/blk-mq.c-1261-\t cpus_equal_capacity(cpu, rq-\u003emq_ctx-\u003ecpu)))\n--\ninclude/linux/sched/topology.h=202=bool cpus_equal_capacity(int this_cpu, int that_cpu);\ninclude/linux/sched/topology.h:203:bool cpus_share_cache(int this_cpu, int that_cpu);\ninclude/linux/sched/topology.h-204-bool cpus_share_resources(int this_cpu, int that_cpu);\n--\nkernel/sched/core.c=3984=bool cpus_equal_capacity(int this_cpu, int that_cpu)\n--\nkernel/sched/core.c-3994-\nkernel/sched/core.c:3995:bool cpus_share_cache(int this_cpu, int that_cpu)\nkernel/sched/core.c-3996-{\n--\nkernel/sched/core.c=4015=static inline bool ttwu_queue_cond(struct task_struct *p, int cpu)\n--\nkernel/sched/core.c-4042-\t */\nkernel/sched/core.c:4043:\tif (!cpus_share_cache(this_cpu, cpu))\nkernel/sched/core.c-4044-\t\treturn true;\n--\nkernel/sched/ext/idle.c=458=s32 scx_select_cpu_dfl(struct task_struct *p, s32 prev_cpu, u64 wake_flags,\n--\nkernel/sched/ext/idle.c-533-\t\tcpu = smp_processor_id();\nkernel/sched/ext/idle.c:534:\t\tif (is_prev_allowed \u0026\u0026 cpus_share_cache(cpu, prev_cpu) \u0026\u0026\nkernel/sched/ext/idle.c-535-\t\t scx_idle_test_and_clear_cpu(prev_cpu)) {\n--\nkernel/sched/fair.c=8350=wake_affine_idle(int this_cpu, int prev_cpu, int sync)\n--\nkernel/sched/fair.c-8363-\t */\nkernel/sched/fair.c:8364:\tif (available_idle_cpu(this_cpu) \u0026\u0026 cpus_share_cache(this_cpu, prev_cpu))\nkernel/sched/fair.c-8365-\t\treturn available_idle_cpu(prev_cpu) ? prev_cpu : this_cpu;\n--\nkernel/sched/fair.c=8949=static int select_idle_sibling(struct task_struct *p, int prev, int target)\n--\nkernel/sched/fair.c-8978-\t */\nkernel/sched/fair.c:8979:\tif (prev != target \u0026\u0026 cpus_share_cache(prev, target) \u0026\u0026\nkernel/sched/fair.c-8980-\t choose_idle_cpu(prev, p) \u0026\u0026\n--\nkernel/sched/fair.c-9010-\t recent_used_cpu != target \u0026\u0026\nkernel/sched/fair.c:9011:\t cpus_share_cache(recent_used_cpu, target) \u0026\u0026\nkernel/sched/fair.c-9012-\t choose_idle_cpu(recent_used_cpu, p) \u0026\u0026\n--\nkernel/sched/fair.c-9050-\nkernel/sched/fair.c:9051:\t\tif (!has_idle_core \u0026\u0026 cpus_share_cache(prev, target)) {\nkernel/sched/fair.c-9052-\t\t\ti = select_idle_smt(p, sd, prev);\n--\nkernel/sched/fair.c=10698=static enum llc_mig can_migrate_llc_task(int src_cpu, int dst_cpu,\n--\nkernel/sched/fair.c-10709-\tcpu = READ_ONCE(mm-\u003esc_stat.cpu);\nkernel/sched/fair.c:10710:\tif (cpu \u003c 0 || cpus_share_cache(src_cpu, dst_cpu))\nkernel/sched/fair.c-10711-\t\treturn mig_unrestricted;\n--\nkernel/sched/fair.c-10720-\nkernel/sched/fair.c:10721:\tif (cpus_share_cache(dst_cpu, cpu))\nkernel/sched/fair.c-10722-\t\tto_pref = true;\nkernel/sched/fair.c:10723:\telse if (cpus_share_cache(src_cpu, cpu))\nkernel/sched/fair.c-10724-\t\tto_pref = false;\n--\nkernel/sched/fair.c=10741=alb_break_llc(struct lb_env *env)\n--\nkernel/sched/fair.c-10745-\nkernel/sched/fair.c:10746:\tif (cpus_share_cache(env-\u003esrc_cpu, env-\u003edst_cpu))\nkernel/sched/fair.c-10747-\t\treturn false;\n--\nkernel/sched/topology.c=660=static void destroy_sched_domains(struct sched_domain *sd)\n--\nkernel/sched/topology.c-672- * of the domain), this allows us to quickly tell if two CPUs are in the same\nkernel/sched/topology.c:673: * cache domain, see cpus_share_cache().\nkernel/sched/topology.c-674- */\n--\nkernel/workqueue.c=8539=static void __init precompute_cache_shard_ids(void)\n--\nkernel/workqueue.c-8561-/*\nkernel/workqueue.c:8562: * cpus_share_cache_shard - test whether two CPUs belong to the same cache shard\nkernel/workqueue.c-8563- *\n--\nkernel/workqueue.c-8566- */\nkernel/workqueue.c:8567:static bool __init cpus_share_cache_shard(int cpu0, int cpu1)\nkernel/workqueue.c-8568-{\nkernel/workqueue.c:8569:\tif (!cpus_share_cache(cpu0, cpu1))\nkernel/workqueue.c-8570-\t\treturn false;\n--\nkernel/workqueue.c=8582=void __init workqueue_init_topology(void)\n--\nkernel/workqueue.c-8588-\tinit_pod_type(\u0026wq_pod_types[WQ_AFFN_SMT], cpus_share_smt);\nkernel/workqueue.c:8589:\tinit_pod_type(\u0026wq_pod_types[WQ_AFFN_CACHE], cpus_share_cache);\nkernel/workqueue.c-8590-\tprecompute_cache_shard_ids();\nkernel/workqueue.c:8591:\tinit_pod_type(\u0026wq_pod_types[WQ_AFFN_CACHE_SHARD], cpus_share_cache_shard);\nkernel/workqueue.c-8592-\tinit_pod_type(\u0026wq_pod_types[WQ_AFFN_NUMA], cpus_share_numa);\n--\nnet/core/dev.c=5275=bool rps_llc_check(u32 old_val, u32 new_val)\n--\nnet/core/dev.c-5300-\t\t */\nnet/core/dev.c:5301:\t\tif (!cpus_share_cache(old_cpu, new_cpu))\nnet/core/dev.c-5302-\t\t\treturn true;\n"
} |
| 19/2 | 2026/09/02 14:08 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 16264 output: 36 thoughts: 22 Thoughts: Here's my take on the thought process, as if it were my own: **Diving into the Scheduler Core** Okay, so we need to understand how CPU cache sharing is handled within the Linux kernel scheduler. Let's start with a look at `kernel/sched/core.c`. Specifically, I'm interested in the functions and data structures involved with `cpus_share_cache`. This is the likely point of control. I know this area fairly well, so I'm expecting to find the key pieces related to NUMA node awareness and how we handle core-sharing affinity. Time to start digging! |
| 20/2 | 2026/09/02 14:09 | tool | read-file |
0mArgs:{
"File": "kernel/sched/core.c",
"FirstLine": 3990,
"LineCount": 25
}Results: {
"Contents": "3990:\t\t\treturn true;\n3991:\t\n3992:\t\treturn arch_scale_cpu_capacity(this_cpu) == arch_scale_cpu_capacity(that_cpu);\n3993:\t}\n3994:\t\n3995:\tbool cpus_share_cache(int this_cpu, int that_cpu)\n3996:\t{\n3997:\t\tif (this_cpu == that_cpu)\n3998:\t\t\treturn true;\n3999:\t\n4000:\t\treturn per_cpu(sd_llc_id, this_cpu) == per_cpu(sd_llc_id, that_cpu);\n4001:\t}\n4002:\t\n4003:\t/*\n4004:\t * Whether CPUs are share cache resources, which means LLC on non-cluster\n4005:\t * machines and LLC tag or L2 on machines with clusters.\n4006:\t */\n4007:\tbool cpus_share_resources(int this_cpu, int that_cpu)\n4008:\t{\n4009:\t\tif (this_cpu == that_cpu)\n4010:\t\t\treturn true;\n4011:\t\n4012:\t\treturn per_cpu(sd_share_id, this_cpu) == per_cpu(sd_share_id, that_cpu);\n4013:\t}\n4014:\t\n"
} |
| 21/2 | 2026/09/02 14:09 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 16829 output: 40 thoughts: 29 Thoughts: Here's my summary, as if I were thinking these thoughts: **Investigating RPS Feature Affinity in Net/Core** Okay, so I need to dig into how RPS (Receive Packet Steering) handles LLC (Link Layer Control) affinity. The best place to start, as usual, is the core networking subsystem. Let's get right to it – I'm thinking I should take a look at the `net/core/sysctl_net_core.c` file. Specifically, I'm going to scan that file for the `rps_feat_llc_affinity` variable or function. That's likely where the control mechanisms for this particular RPS feature are defined, right? I'm hoping to get a clearer understanding of how it's enabled, disabled, configured, and how it interacts with the rest of the RPS framework. Let's see what that code reveals. |
| 22/2 | 2026/09/02 14:09 | tool | read-file |
0mArgs:{
"File": "net/core/sysctl_net_core.c",
"FirstLine": 545,
"LineCount": 35
}Results: {
"Contents": " 545:\t\t\t.procname\t= \"message_burst\",\n 546:\t\t\t.data\t\t= \u0026net_ratelimit_state.burst,\n 547:\t\t\t.maxlen\t\t= sizeof(int),\n 548:\t\t\t.mode\t\t= 0644,\n 549:\t\t\t.proc_handler\t= proc_dointvec,\n 550:\t\t},\n 551:\t#ifdef CONFIG_RPS\n 552:\t\t{\n 553:\t\t\t.procname\t= \"rps_sock_flow_entries\",\n 554:\t\t\t.maxlen\t\t= sizeof(int),\n 555:\t\t\t.mode\t\t= 0644,\n 556:\t\t\t.proc_handler\t= rps_sock_flow_sysctl\n 557:\t\t},\n 558:\t\t{\n 559:\t\t\t.procname\t= \"rps_feat_llc_affinity\",\n 560:\t\t\t.data\t\t= \u0026rps_feat_llc_affinity.key,\n 561:\t\t\t.maxlen\t\t= sizeof(rps_feat_llc_affinity.key),\n 562:\t\t\t.mode\t\t= 0644,\n 563:\t\t\t.proc_handler\t= proc_do_static_key\n 564:\t\t},\n 565:\t#endif\n 566:\t#ifdef CONFIG_NET_FLOW_LIMIT\n 567:\t\t{\n 568:\t\t\t.procname\t= \"flow_limit_cpu_bitmap\",\n 569:\t\t\t.mode\t\t= 0644,\n 570:\t\t\t.proc_handler\t= flow_limit_cpu_sysctl\n 571:\t\t},\n 572:\t\t{\n 573:\t\t\t.procname\t= \"flow_limit_table_len\",\n 574:\t\t\t.data\t\t= \u0026netdev_flow_limit_table_len,\n 575:\t\t\t.maxlen\t\t= sizeof(int),\n 576:\t\t\t.mode\t\t= 0644,\n 577:\t\t\t.proc_handler\t= flow_limit_table_len_sysctl\n 578:\t\t},\n 579:\t#endif /* CONFIG_NET_FLOW_LIMIT */\n"
} |
| 23/2 | 2026/09/02 14:09 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 17655 output: 167 thoughts: 284 Thoughts: **My Analysis of the RPS Fuzzing Scenario** Okay, let's break this down. First, I need to confirm the configuration requirements for the `sysctl` interface. While usually `SYSCTL` or `PROC_SYSCTL` handle these, I need to double-check that this is the case here. Next, I see that the configuration `RPS` relies on both `SMP` (symmetric multiprocessing) and `SYSFS` (sysfs file system). So I'll need to enable those configs; therefore, `EnableConfigs`: `["RPS", "SMP", "SYSFS"]`. Now, the interesting part: the focused symbols. It looks like we have a few newly added or modified symbols in `net/core/dev.c`. Specifically, I'm noting that `rps_llc_check` is a new function, and both `sock_rps_record_flow` and `sock_rps_record_flow_hash` are exported functions, also in that file. These functions are prime targets for fuzzing. They represent a significant change or addition to the code. Given this information, and remembering the instructions I received, I'm confident that this is worth fuzzing. The `FocusSymbols` are clearly defined: `["rps_llc_check", "sock_rps_record_flow", "sock_rps_record_flow_hash"]`. I know the necessary `EnableConfigs`: `["RPS", "SMP", "SYSFS"]`. Now, the crucial step: I need to clearly articulate the rationale, explain what these symbols do, and connect them to the relevant configs. I'm ready to call `set-results`! |
| 24/2 | 2026/09/02 14:09 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"RPS",
"SMP",
"SYSFS"
],
"FocusSymbols": [
"rps_llc_check",
"sock_rps_record_flow",
"sock_rps_record_flow_hash"
],
"Reasoning": "The patch introduces an LLC (Last Level Cache) affinity check for RPS/RFS flow updates controlled via a new sysctl (net.core.rps_feat_llc_affinity). It modifies core packet/socket receive steering logic in net/core/dev.c and moves sock_rps_record_flow/sock_rps_record_flow_hash to non-inline exported functions. This logic is fully reachable in standard virtualized environments.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"RPS",
"SMP",
"SYSFS"
],
"FocusSymbols": [
"rps_llc_check",
"sock_rps_record_flow",
"sock_rps_record_flow_hash"
],
"Reasoning": "The patch introduces an LLC (Last Level Cache) affinity check for RPS/RFS flow updates controlled via a new sysctl (net.core.rps_feat_llc_affinity). It modifies core packet/socket receive steering logic in net/core/dev.c and moves sock_rps_record_flow/sock_rps_record_flow_hash to non-inline exported functions. This logic is fully reachable in standard virtualized environments.",
"WorthFuzzing": true
} |
| 25/1 | 2026/09/02 14:09 | action | If |
2mArgs:{
"WorthFuzzing": true
}Results: null |
| 26/2 | 2026/09/02 14:09 | agent | kmsan-evaluator |
2mModel:core Args: null Results: {
"KMSANReasoning": "The patch introduces an LLC-affinity-based optimization for Receive Packet Steering / Receive Flow Steering (RFS) flow table updates, adds a corresponding sysctl entry (rps_feat_llc_affinity) handled by proc_do_static_key, and out-of-lines/exports sock_rps_record_flow{,_hash}.\n\nAll operations in this patch work with scalar values (u32 hash/cpu IDs) and static keys. No new memory allocations, uninitialized stack or heap buffers, struct padding exposures, or kernel-to-user copy paths are introduced or modified. There is no risk of uninitialized memory reads or information leaks, so a dedicated KMSAN fuzzing session is not warranted; standard KASAN and kernel testing are sufficient.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
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 be9b9f94c3c4401362c8a44e77fd905493be8626
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Sep 2 14:05:49 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/net/rps.h b/include/net/rps.h
index e33c6a2fa8bbc..6dacf0888a6c2 100644
--- a/include/net/rps.h
+++ b/include/net/rps.h
@@ -12,6 +12,7 @@
extern struct static_key_false rps_needed;
extern struct static_key_false rfs_needed;
+extern struct static_key_false rps_feat_llc_affinity;
/*
* This structure holds an RPS map which can be of variable length. The
@@ -55,11 +56,14 @@ struct rps_sock_flow_table {
#define RPS_NO_CPU 0xffff
+bool rps_llc_check(u32 old_val, u32 new_val);
+
static inline void rps_record_sock_flow(rps_tag_ptr tag_ptr, u32 hash)
{
unsigned int index = hash & rps_tag_to_mask(tag_ptr);
u32 val = hash & ~net_hotdata.rps_cpu_mask;
struct rps_sock_flow_table *table;
+ u32 old_val;
/* We only give a hint, preemption can change CPU under us */
val |= raw_smp_processor_id();
@@ -68,7 +72,8 @@ static inline void rps_record_sock_flow(rps_tag_ptr tag_ptr, u32 hash)
/* The following WRITE_ONCE() is paired with the READ_ONCE()
* here, and another one in get_rps_cpu().
*/
- if (READ_ONCE(table[index].ent) != val)
+ old_val = READ_ONCE(table[index].ent);
+ if (old_val != val && rps_llc_check(old_val, val))
WRITE_ONCE(table[index].ent, val);
}
@@ -136,25 +141,8 @@ static inline bool rfs_is_needed(void)
#endif
}
-static inline void sock_rps_record_flow_hash(__u32 hash)
-{
-#ifdef CONFIG_RPS
- if (!rfs_is_needed())
- return;
-
- _sock_rps_record_flow_hash(hash);
-#endif
-}
-
-static inline void sock_rps_record_flow(const struct sock *sk)
-{
-#ifdef CONFIG_RPS
- if (!rfs_is_needed())
- return;
-
- _sock_rps_record_flow(sk);
-#endif
-}
+void sock_rps_record_flow_hash(__u32 hash);
+void sock_rps_record_flow(const struct sock *sk);
static inline void sock_rps_delete_flow(const struct sock *sk)
{
diff --git a/net/core/dev.c b/net/core/dev.c
index 38336858c168f..b27839eb594d6 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5052,6 +5052,8 @@ struct static_key_false rps_needed __read_mostly;
EXPORT_SYMBOL(rps_needed);
struct static_key_false rfs_needed __read_mostly;
EXPORT_SYMBOL(rfs_needed);
+struct static_key_false rps_feat_llc_affinity __read_mostly;
+EXPORT_SYMBOL(rps_feat_llc_affinity);
static u32 rfs_slot(u32 hash, rps_tag_ptr tag_ptr)
{
@@ -5263,6 +5265,48 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb,
return cpu;
}
+/**
+ * rps_llc_check - determine if RPS flow table should be updated.
+ * @old_val: previous flow record value.
+ * @new_val: target flow record value.
+ *
+ * Return: true if the record needs an update, false otherwise.
+ */
+bool rps_llc_check(u32 old_val, u32 new_val)
+{
+ u32 old_cpu = old_val & net_hotdata.rps_cpu_mask;
+ u32 new_cpu = new_val & net_hotdata.rps_cpu_mask;
+
+ /*
+ * RPS LLC Affinity Feature:
+ * Reduce RFS/ARFS flow updates by checking LLC affinity.
+ *
+ * Frequent flow table updates can trigger constant hardware steering
+ * reconfigurations (e.g., ndo_rx_flow_steer), leading to significant
+ * contention on driver internal locks (like mlx5's arfs_lock).
+ *
+ * This strategy only updates the flow record if it migrates across LLC
+ * boundaries. This minimizes expensive hardware updates while preserving
+ * cache locality for the application.
+ */
+ if (static_branch_unlikely(&rps_feat_llc_affinity)) {
+ /* Force update if the recorded CPU is invalid or has gone offline */
+ if (old_cpu >= nr_cpu_ids || !cpu_active(old_cpu))
+ return true;
+
+ /*
+ * If CPUs do not share a cache, allow the update to prevent
+ * expensive remote memory accesses and cache misses.
+ */
+ if (!cpus_share_cache(old_cpu, new_cpu))
+ return true;
+
+ return false;
+ }
+
+ return true;
+}
+
#ifdef CONFIG_RFS_ACCEL
/**
@@ -5318,6 +5362,28 @@ static void rps_trigger_softirq(void *data)
#endif /* CONFIG_RPS */
+void sock_rps_record_flow_hash(__u32 hash)
+{
+#ifdef CONFIG_RPS
+ if (!rfs_is_needed())
+ return;
+
+ _sock_rps_record_flow_hash(hash);
+#endif
+}
+EXPORT_SYMBOL(sock_rps_record_flow_hash);
+
+void sock_rps_record_flow(const struct sock *sk)
+{
+#ifdef CONFIG_RPS
+ if (!rfs_is_needed())
+ return;
+
+ _sock_rps_record_flow(sk);
+#endif
+}
+EXPORT_SYMBOL(sock_rps_record_flow);
+
/* Called from hardirq (IPI) context */
static void trigger_rx_softirq(void *data)
{
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index eb35da3556f4a..9b6702f5eab19 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -210,6 +210,7 @@ static int rps_sock_flow_sysctl(const struct ctl_table *table, int write,
kvfree_rcu_mightsleep(tofree);
return ret;
}
+
#endif /* CONFIG_RPS */
#ifdef CONFIG_NET_FLOW_LIMIT
@@ -554,6 +555,13 @@ static struct ctl_table net_core_table[] = {
.mode = 0644,
.proc_handler = rps_sock_flow_sysctl
},
+ {
+ .procname = "rps_feat_llc_affinity",
+ .data = &rps_feat_llc_affinity.key,
+ .maxlen = sizeof(rps_feat_llc_affinity.key),
+ .mode = 0644,
+ .proc_handler = proc_do_static_key
+ },
#endif
#ifdef CONFIG_NET_FLOW_LIMIT
{
|
| 27/3 | 2026/09/02 14:09 | llm | kmsan-evaluator |
1mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 3940 output: 21 thoughts: 714 Thoughts: **KMSAN Risk Assessment for RPS LLC Affinity Patch** Okay, let's dive into this patch and see if it introduces any uninitialized memory vulnerabilities that KMSAN would flag. First, I'll meticulously go over the diff, focusing on potential areas for concern. Looking at `include/net/rps.h`, it introduces `rps_feat_llc_affinity` and the `rps_llc_check` function. The key here is that `old_val` in `rps_record_sock_flow` is read using `READ_ONCE`, and then passed to `rps_llc_check` before any writes. Good, seems safe on the surface. Also, the function definitions were moved from the header file to `net/core/dev.c`. Moving on to `net/core/dev.c`, we see the definition of `rps_feat_llc_affinity` and the implementation of `rps_llc_check`. This function is where the core logic resides. The check extracts CPU affinity information based on a CPU mask. The use of `static_branch_unlikely` is interesting, and the function returns true if the old CPU is invalid or the CPUs don't share the same cache. The `net_hotdata.rps_cpu_mask` is also worth a closer look in other areas. The functions added to this file are now exported. Finally, in `net/core/sysctl_net_core.c`, the patch adds a sysctl entry for `rps_feat_llc_affinity` using `proc_do_static_key`. This is where my focus sharpens. The biggest risk here is uninitialized memory being exposed to userspace via the sysctl interface. I need to make sure that the `proc_do_static_key` handler is safe. Let me check the other usages of `proc_do_static_key` elsewhere in the kernel to see if there are any standard practices or potential risks. Now, let's specifically look for any risks. Are there uninitialized kernel data copied to user space? `proc_do_static_key` reads from and writes to `rps_feat_llc_affinity.key`. Is this a standard/safe practice? I need to verify that `proc_do_static_key` initializes this correctly. I will do a quick grep on the codebase for `proc_do_static_key` to be extra sure. As for other potential KMSAN flags - are there any new allocations, stack buffers, struct padding, or uninitialized reads? `rps_llc_check` operates on scalar `u32` arguments which are initialized, so that is not a concern. The `old_val` is initialized by `READ_ONCE(table[index].ent)` and `val` is initialized by bitwise operations so this doesn't seem like there are uninitialized reads on this front. Furthermore, the functions don't copy heap/stack structures to user space. Therefore, I don't see any obvious padding issues or uninitialized branches either. Overall, it *appears* safe, but I'll double-check `proc_do_static_key` usages to confirm. |
| 28/3 | 2026/09/02 14:10 | tool | grepper |
0mArgs:{
"Expression": "proc_do_static_key"
}Results: {
"Output": "include/linux/sysctl.h=97=int proc_do_large_bitmap(const struct ctl_table *ctl, int dir, void *buf,\ninclude/linux/sysctl.h-98-\t\t\t size_t *lenp, loff_t *ppos);\ninclude/linux/sysctl.h:99:int proc_do_static_key(const struct ctl_table *ctl, int dir, void *buf,\ninclude/linux/sysctl.h-100-\t\t size_t *lenp, loff_t *ppos);\n--\nkernel/sysctl.c=1361=int proc_do_large_bitmap(const struct ctl_table *table, int dir,\n--\nkernel/sysctl.c-1369-#if defined(CONFIG_SYSCTL)\nkernel/sysctl.c:1370:int proc_do_static_key(const struct ctl_table *table, int dir,\nkernel/sysctl.c-1371-\t\t void *buffer, size_t *lenp, loff_t *ppos)\n--\nlib/alloc_tag.c=935=EXPORT_SYMBOL(page_alloc_tagging_ops);\n--\nlib/alloc_tag.c-938-/*\nlib/alloc_tag.c:939: * Not using proc_do_static_key() directly to prevent enabling profiling\nlib/alloc_tag.c-940- * after it was shut down.\n--\nlib/alloc_tag.c=942=static int proc_mem_profiling_handler(const struct ctl_table *table, int write,\n--\nlib/alloc_tag.c-961-\nlib/alloc_tag.c:962:\treturn proc_do_static_key(table, write, buffer, lenp, ppos);\nlib/alloc_tag.c-963-}\n--\nmm/alloc_tag.c=1268=EXPORT_SYMBOL(page_alloc_tagging_ops);\n--\nmm/alloc_tag.c-1271-/*\nmm/alloc_tag.c:1272: * Not using proc_do_static_key() directly to prevent enabling profiling\nmm/alloc_tag.c-1273- * after it was shut down.\n--\nmm/alloc_tag.c=1275=static int proc_mem_profiling_handler(const struct ctl_table *table, int write,\n--\nmm/alloc_tag.c-1294-\nmm/alloc_tag.c:1295:\treturn proc_do_static_key(table, write, buffer, lenp, ppos);\nmm/alloc_tag.c-1296-}\n--\nmm/alloc_tag.c=1299=static const struct ctl_table memory_allocation_profiling_sysctls[] = {\n--\nmm/alloc_tag.c-1309-\t\t.mode\t\t= 0444,\nmm/alloc_tag.c:1310:\t\t.proc_handler\t= proc_do_static_key,\nmm/alloc_tag.c-1311-\t},\n--\nnet/core/sysctl_net_core.c=431=static struct ctl_table net_core_table[] = {\n--\nnet/core/sysctl_net_core.c-562-\t\t.mode\t\t= 0644,\nnet/core/sysctl_net_core.c:563:\t\t.proc_handler\t= proc_do_static_key\nnet/core/sysctl_net_core.c-564-\t},\n--\nnet/core/sysctl_net_core.c-659-\t\t.mode\t\t= 0644,\nnet/core/sysctl_net_core.c:660:\t\t.proc_handler\t= proc_do_static_key,\nnet/core/sysctl_net_core.c-661-\t},\n"
} |
| 29/3 | 2026/09/02 14:10 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 6338 output: 20 thoughts: 21 Thoughts: Here's my summary, as though I'm thinking through this process: **Diving into CPU Cache Sharing** Okay, so I'm looking at `cpus_share_cache(old_cpu, new_cpu)`. I've got to really understand the cache topology here. What am I really trying to figure out? I assume this function's the lynchpin, the place where we determine if moving from `old_cpu` to `new_cpu` has any potential performance ramifications stemming from cache contention, or cache affinity issues. I'm going to quickly check the definition of this function and the related data structures to confirm my assumptions. Let's see... is it as simple as checking whether they're on the same NUMA node and share the same levels of cache? Or is it more complex, involving some kind of nuanced analysis of cache hierarchy and coherence domains? I've got to remember to consider the impact of hyperthreading too. Does this function account for that, or is it abstracted away at a lower level? Alright, let's trace this function and its callers to see what the context is. I suspect it's involved in something like CPU hotplugging, or maybe even task migration. Knowing that context will give me clues about the severity of any potential cache-related problems. |
| 30/3 | 2026/09/02 14:10 | tool | grepper |
0mArgs:{
"Expression": "cpus_share_cache"
}Results: {
"Output": "block/blk-mq.c=1241=static inline bool blk_mq_complete_need_ipi(struct request *rq)\n--\nblock/blk-mq.c-1259-\t (!test_bit(QUEUE_FLAG_SAME_FORCE, \u0026rq-\u003eq-\u003equeue_flags) \u0026\u0026\nblock/blk-mq.c:1260:\t cpus_share_cache(cpu, rq-\u003emq_ctx-\u003ecpu) \u0026\u0026\nblock/blk-mq.c-1261-\t cpus_equal_capacity(cpu, rq-\u003emq_ctx-\u003ecpu)))\n--\ninclude/linux/sched/topology.h=202=bool cpus_equal_capacity(int this_cpu, int that_cpu);\ninclude/linux/sched/topology.h:203:bool cpus_share_cache(int this_cpu, int that_cpu);\ninclude/linux/sched/topology.h-204-bool cpus_share_resources(int this_cpu, int that_cpu);\n--\nkernel/sched/core.c=3984=bool cpus_equal_capacity(int this_cpu, int that_cpu)\n--\nkernel/sched/core.c-3994-\nkernel/sched/core.c:3995:bool cpus_share_cache(int this_cpu, int that_cpu)\nkernel/sched/core.c-3996-{\n--\nkernel/sched/core.c=4015=static inline bool ttwu_queue_cond(struct task_struct *p, int cpu)\n--\nkernel/sched/core.c-4042-\t */\nkernel/sched/core.c:4043:\tif (!cpus_share_cache(this_cpu, cpu))\nkernel/sched/core.c-4044-\t\treturn true;\n--\nkernel/sched/ext/idle.c=458=s32 scx_select_cpu_dfl(struct task_struct *p, s32 prev_cpu, u64 wake_flags,\n--\nkernel/sched/ext/idle.c-533-\t\tcpu = smp_processor_id();\nkernel/sched/ext/idle.c:534:\t\tif (is_prev_allowed \u0026\u0026 cpus_share_cache(cpu, prev_cpu) \u0026\u0026\nkernel/sched/ext/idle.c-535-\t\t scx_idle_test_and_clear_cpu(prev_cpu)) {\n--\nkernel/sched/fair.c=8350=wake_affine_idle(int this_cpu, int prev_cpu, int sync)\n--\nkernel/sched/fair.c-8363-\t */\nkernel/sched/fair.c:8364:\tif (available_idle_cpu(this_cpu) \u0026\u0026 cpus_share_cache(this_cpu, prev_cpu))\nkernel/sched/fair.c-8365-\t\treturn available_idle_cpu(prev_cpu) ? prev_cpu : this_cpu;\n--\nkernel/sched/fair.c=8949=static int select_idle_sibling(struct task_struct *p, int prev, int target)\n--\nkernel/sched/fair.c-8978-\t */\nkernel/sched/fair.c:8979:\tif (prev != target \u0026\u0026 cpus_share_cache(prev, target) \u0026\u0026\nkernel/sched/fair.c-8980-\t choose_idle_cpu(prev, p) \u0026\u0026\n--\nkernel/sched/fair.c-9010-\t recent_used_cpu != target \u0026\u0026\nkernel/sched/fair.c:9011:\t cpus_share_cache(recent_used_cpu, target) \u0026\u0026\nkernel/sched/fair.c-9012-\t choose_idle_cpu(recent_used_cpu, p) \u0026\u0026\n--\nkernel/sched/fair.c-9050-\nkernel/sched/fair.c:9051:\t\tif (!has_idle_core \u0026\u0026 cpus_share_cache(prev, target)) {\nkernel/sched/fair.c-9052-\t\t\ti = select_idle_smt(p, sd, prev);\n--\nkernel/sched/fair.c=10698=static enum llc_mig can_migrate_llc_task(int src_cpu, int dst_cpu,\n--\nkernel/sched/fair.c-10709-\tcpu = READ_ONCE(mm-\u003esc_stat.cpu);\nkernel/sched/fair.c:10710:\tif (cpu \u003c 0 || cpus_share_cache(src_cpu, dst_cpu))\nkernel/sched/fair.c-10711-\t\treturn mig_unrestricted;\n--\nkernel/sched/fair.c-10720-\nkernel/sched/fair.c:10721:\tif (cpus_share_cache(dst_cpu, cpu))\nkernel/sched/fair.c-10722-\t\tto_pref = true;\nkernel/sched/fair.c:10723:\telse if (cpus_share_cache(src_cpu, cpu))\nkernel/sched/fair.c-10724-\t\tto_pref = false;\n--\nkernel/sched/fair.c=10741=alb_break_llc(struct lb_env *env)\n--\nkernel/sched/fair.c-10745-\nkernel/sched/fair.c:10746:\tif (cpus_share_cache(env-\u003esrc_cpu, env-\u003edst_cpu))\nkernel/sched/fair.c-10747-\t\treturn false;\n--\nkernel/sched/topology.c=660=static void destroy_sched_domains(struct sched_domain *sd)\n--\nkernel/sched/topology.c-672- * of the domain), this allows us to quickly tell if two CPUs are in the same\nkernel/sched/topology.c:673: * cache domain, see cpus_share_cache().\nkernel/sched/topology.c-674- */\n--\nkernel/workqueue.c=8539=static void __init precompute_cache_shard_ids(void)\n--\nkernel/workqueue.c-8561-/*\nkernel/workqueue.c:8562: * cpus_share_cache_shard - test whether two CPUs belong to the same cache shard\nkernel/workqueue.c-8563- *\n--\nkernel/workqueue.c-8566- */\nkernel/workqueue.c:8567:static bool __init cpus_share_cache_shard(int cpu0, int cpu1)\nkernel/workqueue.c-8568-{\nkernel/workqueue.c:8569:\tif (!cpus_share_cache(cpu0, cpu1))\nkernel/workqueue.c-8570-\t\treturn false;\n--\nkernel/workqueue.c=8582=void __init workqueue_init_topology(void)\n--\nkernel/workqueue.c-8588-\tinit_pod_type(\u0026wq_pod_types[WQ_AFFN_SMT], cpus_share_smt);\nkernel/workqueue.c:8589:\tinit_pod_type(\u0026wq_pod_types[WQ_AFFN_CACHE], cpus_share_cache);\nkernel/workqueue.c-8590-\tprecompute_cache_shard_ids();\nkernel/workqueue.c:8591:\tinit_pod_type(\u0026wq_pod_types[WQ_AFFN_CACHE_SHARD], cpus_share_cache_shard);\nkernel/workqueue.c-8592-\tinit_pod_type(\u0026wq_pod_types[WQ_AFFN_NUMA], cpus_share_numa);\n--\nnet/core/dev.c=5275=bool rps_llc_check(u32 old_val, u32 new_val)\n--\nnet/core/dev.c-5300-\t\t */\nnet/core/dev.c:5301:\t\tif (!cpus_share_cache(old_cpu, new_cpu))\nnet/core/dev.c-5302-\t\t\treturn true;\n"
} |
| 31/3 | 2026/09/02 14:10 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 8667 output: 36 thoughts: 22 Thoughts: Here's my summary, as though I'm thinking through this process: **Diving into the Scheduler Core** Okay, so we're talking about CPU cache sharing, which makes me think `kernel/sched/core.c` is the likely place to start looking. Specifically, the function `cpus_share_cache`. That name is pretty indicative of what we're after, so that's where I'll concentrate my efforts. Let's see what the code around there tells us about how the kernel handles this relationship between CPUs and their caches. Understanding how the scheduler views and manages cache sharing is crucial for performance optimization, especially on modern multi-core systems. I want to see how it's implemented and how the kernel reasons about it when making scheduling decisions. I'll be looking for things like data structures, locking mechanisms, and the actual logic for determining whether or not CPUs are sharing a cache at any given moment. |
| 32/3 | 2026/09/02 14:11 | tool | read-file |
0mArgs:{
"File": "kernel/sched/core.c",
"FirstLine": 3990,
"LineCount": 25
}Results: {
"Contents": "3990:\t\t\treturn true;\n3991:\t\n3992:\t\treturn arch_scale_cpu_capacity(this_cpu) == arch_scale_cpu_capacity(that_cpu);\n3993:\t}\n3994:\t\n3995:\tbool cpus_share_cache(int this_cpu, int that_cpu)\n3996:\t{\n3997:\t\tif (this_cpu == that_cpu)\n3998:\t\t\treturn true;\n3999:\t\n4000:\t\treturn per_cpu(sd_llc_id, this_cpu) == per_cpu(sd_llc_id, that_cpu);\n4001:\t}\n4002:\t\n4003:\t/*\n4004:\t * Whether CPUs are share cache resources, which means LLC on non-cluster\n4005:\t * machines and LLC tag or L2 on machines with clusters.\n4006:\t */\n4007:\tbool cpus_share_resources(int this_cpu, int that_cpu)\n4008:\t{\n4009:\t\tif (this_cpu == that_cpu)\n4010:\t\t\treturn true;\n4011:\t\n4012:\t\treturn per_cpu(sd_share_id, this_cpu) == per_cpu(sd_share_id, that_cpu);\n4013:\t}\n4014:\t\n"
} |
| 33/3 | 2026/09/02 14:11 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 9298 output: 34 thoughts: 23 Thoughts: Here's my take on those thoughts, tailored for an expert audience: **Diving into RPS Flow Recording** Okay, so I need to refresh my memory on how RPS flow recording works, specifically, the `rps_record_sock_flow` function. Right, that's defined in `include/net/rps.h`. Let's pull that up and see exactly what it's doing. I want to quickly review its arguments, its expected behavior given different socket types and flow contexts, and how it interacts with the RPS core data structures. Understanding this function is crucial for diagnosing potential performance bottlenecks or subtle behavior related to flow affinity. I'm especially interested in how it handles shared sockets and whether it correctly interacts with various offload configurations. Gotta ensure everything's properly accounted for from a locking and concurrency perspective too. Time to dig in! |
| 34/3 | 2026/09/02 14:11 | tool | read-file |
0mArgs:{
"File": "include/net/rps.h",
"FirstLine": 55,
"LineCount": 45
}Results: {
"Contents": " 55:\t};\n 56:\t\n 57:\t#define RPS_NO_CPU 0xffff\n 58:\t\n 59:\tbool rps_llc_check(u32 old_val, u32 new_val);\n 60:\t\n 61:\tstatic inline void rps_record_sock_flow(rps_tag_ptr tag_ptr, u32 hash)\n 62:\t{\n 63:\t\tunsigned int index = hash \u0026 rps_tag_to_mask(tag_ptr);\n 64:\t\tu32 val = hash \u0026 ~net_hotdata.rps_cpu_mask;\n 65:\t\tstruct rps_sock_flow_table *table;\n 66:\t\tu32 old_val;\n 67:\t\n 68:\t\t/* We only give a hint, preemption can change CPU under us */\n 69:\t\tval |= raw_smp_processor_id();\n 70:\t\n 71:\t\ttable = rps_tag_to_table(tag_ptr);\n 72:\t\t/* The following WRITE_ONCE() is paired with the READ_ONCE()\n 73:\t\t * here, and another one in get_rps_cpu().\n 74:\t\t */\n 75:\t\told_val = READ_ONCE(table[index].ent);\n 76:\t\tif (old_val != val \u0026\u0026 rps_llc_check(old_val, val))\n 77:\t\t\tWRITE_ONCE(table[index].ent, val);\n 78:\t}\n 79:\t\n 80:\tstatic inline void _sock_rps_record_flow_hash(__u32 hash)\n 81:\t{\n 82:\t\trps_tag_ptr tag_ptr;\n 83:\t\n 84:\t\tif (!hash)\n 85:\t\t\treturn;\n 86:\t\trcu_read_lock();\n 87:\t\ttag_ptr = READ_ONCE(net_hotdata.rps_sock_flow_table);\n 88:\t\tif (tag_ptr)\n 89:\t\t\trps_record_sock_flow(tag_ptr, hash);\n 90:\t\trcu_read_unlock();\n 91:\t}\n 92:\t\n 93:\tstatic inline void _sock_rps_record_flow(const struct sock *sk)\n 94:\t{\n 95:\t\t/* Reading sk-\u003esk_rxhash might incur an expensive cache line\n 96:\t\t * miss.\n 97:\t\t *\n 98:\t\t * TCP_ESTABLISHED does cover almost all states where RFS\n 99:\t\t * might be useful, and is cheaper [1] than testing :\n"
} |
| 35/3 | 2026/09/02 14:11 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 10213 output: 184 thoughts: 253 Thoughts: **My Assessment of the Code Changes** Okay, let's break this down systematically. This patch is focused on optimizing RFS/ARFS flow updates, specifically by adding a check for LLC affinity (`rps_llc_check`). That's a performance improvement, which is good. We also see a new sysctl boolean knob being introduced, `rps_feat_llc_affinity`. Interesting. Now, I see the inlined functions `sock_rps_record_flow_hash` and `sock_rps_record_flow` are being moved. This is usually to make them callable from other parts of the kernel. Nothing inherently dangerous, but we always have to be careful about where they are moved. The critical questions: Uninitialized memory reads? Nope, thankfully. Passing uninitialized data to userspace or across kernel boundaries? No issues there either. Buffer sizes, allocations, struct layouts? No changes, which is a relief. No new stack allocations, and no obvious risks from uninitialized memory. So, standard memory sanitizers – KASAN, LOCKDEP, UBSAN – plus the standard testing regime should be completely sufficient to catch any potential issues. There are no obvious red flags that would justify invoking KMSAN for this. We aren't dealing with any of the subtle, potentially uninitialized memory hazards that KMSAN is specifically designed to uncover. Therefore, `NeedsKMSAN = false`. We can safely move forward without triggering a KMSAN scan for this patch. I will make sure to clearly articulate this reasoning for the record, explaining exactly why we are confident in relying on existing tools and testing. Then, I need to call `set-results`. |
| 36/3 | 2026/09/02 14:11 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch introduces an LLC-affinity-based optimization for Receive Packet Steering / Receive Flow Steering (RFS) flow table updates, adds a corresponding sysctl entry (rps_feat_llc_affinity) handled by proc_do_static_key, and out-of-lines/exports sock_rps_record_flow{,_hash}.\n\nAll operations in this patch work with scalar values (u32 hash/cpu IDs) and static keys. No new memory allocations, uninitialized stack or heap buffers, struct padding exposures, or kernel-to-user copy paths are introduced or modified. There is no risk of uninitialized memory reads or information leaks, so a dedicated KMSAN fuzzing session is not warranted; standard KASAN and kernel testing are sufficient.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch introduces an LLC-affinity-based optimization for Receive Packet Steering / Receive Flow Steering (RFS) flow table updates, adds a corresponding sysctl entry (rps_feat_llc_affinity) handled by proc_do_static_key, and out-of-lines/exports sock_rps_record_flow{,_hash}.\n\nAll operations in this patch work with scalar values (u32 hash/cpu IDs) and static keys. No new memory allocations, uninitialized stack or heap buffers, struct padding exposures, or kernel-to-user copy paths are introduced or modified. There is no risk of uninitialized memory reads or information leaks, so a dedicated KMSAN fuzzing session is not warranted; standard KASAN and kernel testing are sufficient.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|