| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/16 14:48 | flow | patch-triage |
6mArgs:null Results: {
"EnableConfigs": [
"NET_SCHED",
"NET_SCH_TAPRIO"
],
"FocusSymbols": [
"advance_sched",
"taprio_fast_forward_sched",
"taprio_advance_one_entry",
"taprio_change",
"parse_taprio_schedule",
"find_entry_to_transmit",
"get_packet_txtime",
"is_valid_interval"
],
"KMSANReasoning": "The patch modifies net/sched/sch_taprio.c to improve schedule validation, prevent timer storms by enforcing a minimum interval and fast-forwarding laggy schedules, and cleanly roll back device/qdisc configurations on failure during taprio_change().\n\nKMSAN vs KASAN Applicability:\n- Uninitialized memory / KMSAN: All newly introduced stack arrays (`max_sdu`, `fp`) are explicitly zero-initialized (`= { }`). The rollback buffers (`old_tc_to_txq`, `old_prio_tc_map`, `old_cur_txq`) are guarded by the `tc_configured` flag and initialized via `memcpy` before use. Schedule structures are allocated using `kzalloc_obj`. No structures are copied to userspace and no uninitialized fields are read in branching or control flow. Therefore, there are no uninitialized memory usage or kernel-to-user info-leak risks.\n- KASAN / LOCKDEP / Standard detectors: The risks in this patch revolve around list handling of schedule entries, timer expiration math, locking with `q-\u003ecurrent_entry_lock`, and RCU schedule switching. Bugs in these areas (such as use-after-free, double free, deadlocks, or out-of-bounds array access) are effectively detected by KASAN and LOCKDEP.\n\nConsequently, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies the TAPRIO (Time-Aware Priority) traffic control queueing discipline (sch_taprio.c) in the networking subsystem. TAPRIO is fully reachable in standard virtualized environments (e.g. using virtual network devices like veth or dummy via rtnetlink). The patch introduces significant logic changes: schedule advancement and fast-forwarding loops in the hrtimer callback (advance_sched, taprio_fast_forward_sched, taprio_advance_one_entry), schedule validation and entry truncation/deletion in parse_taprio_schedule, interval calculations in find_entry_to_transmit and get_packet_txtime, and error-path rollback in taprio_change. These complex timing, looping, and list-handling changes warrant fuzzing to detect regressions, infinite loops, and race conditions.",
"WorthFuzzing": true
} |
| 1/1 | 2026/10/16 14:48 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit b51f9aac51b713ad5bb922900aeb8687e60cc7e4\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Oct 16 14:48:53 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c\nindex 39ac5b97aa3af..9128f8179a993 100644\n--- a/net/sched/sch_taprio.c\n+++ b/net/sched/sch_taprio.c\n@@ -48,6 +48,10 @@ static struct static_key_false taprio_have_working_mqprio;\n * 60 * 17 \u003e PSEC_PER_NSEC (1000)\n */\n #define TAPRIO_PICOS_PER_BYTE_MIN 17\n+/* Minimum interval for software mode (hrtimer-driven advance_sched) to\n+ * avoid hrtimer interrupt storms and CPU starvation.\n+ */\n+#define TAPRIO_MIN_SW_INTERVAL_NS (10 * NSEC_PER_USEC)\n \n struct sched_entry {\n \t/* Durations between this GCL entry and the GCL entry where the\n@@ -259,6 +263,17 @@ static int length_to_duration(struct taprio_sched *q, int len)\n \treturn div_u64(len * atomic64_read(\u0026q-\u003epicos_per_byte), PSEC_PER_NSEC);\n }\n \n+static int taprio_min_interval(struct taprio_sched *q)\n+{\n+\tint min_duration = length_to_duration(q, ETH_ZLEN);\n+\n+\tif (!FULL_OFFLOAD_IS_ENABLED(q-\u003eflags) \u0026\u0026\n+\t !TXTIME_ASSIST_IS_ENABLED(q-\u003eflags))\n+\t\tmin_duration = max_t(int, min_duration, TAPRIO_MIN_SW_INTERVAL_NS);\n+\n+\treturn min_duration;\n+}\n+\n static int duration_to_length(struct taprio_sched *q, u64 duration)\n {\n \treturn div_u64(duration * PSEC_PER_NSEC, atomic64_read(\u0026q-\u003epicos_per_byte));\n@@ -270,7 +285,8 @@ static int duration_to_length(struct taprio_sched *q, u64 duration)\n */\n static void taprio_update_queue_max_sdu(struct taprio_sched *q,\n \t\t\t\t\tstruct sched_gate_list *sched,\n-\t\t\t\t\tstruct qdisc_size_table *stab)\n+\t\t\t\t\tstruct qdisc_size_table *stab,\n+\t\t\t\t\tconst u32 max_sdu_user[TC_MAX_QUEUE])\n {\n \tstruct net_device *dev = qdisc_dev(q-\u003eroot);\n \tint num_tc = netdev_get_num_tc(dev);\n@@ -280,7 +296,7 @@ static void taprio_update_queue_max_sdu(struct taprio_sched *q,\n \tint tc;\n \n \tfor (tc = 0; tc \u003c num_tc; tc++) {\n-\t\tmax_sdu_from_user = q-\u003emax_sdu[tc] ?: U32_MAX;\n+\t\tmax_sdu_from_user = max_sdu_user[tc] ?: U32_MAX;\n \n \t\t/* TC gate never closes =\u003e keep the queueMaxSDU\n \t\t * selected by the user\n@@ -357,7 +373,7 @@ static struct sched_entry *find_entry_to_transmit(struct sk_buff *skb,\n \t\tcurr_intv_end = get_interval_end_time(sched, admin, entry,\n \t\t\t\t\t\t curr_intv_start);\n \n-\t\tif (ktime_after(curr_intv_start, cycle_end))\n+\t\tif (ktime_compare(curr_intv_start, cycle_end) \u003e= 0)\n \t\t\tbreak;\n \n \t\tif (!(entry-\u003egate_mask \u0026 BIT(tc)) ||\n@@ -366,10 +382,10 @@ static struct sched_entry *find_entry_to_transmit(struct sk_buff *skb,\n \n \t\ttxtime = entry-\u003enext_txtime;\n \n-\t\tif (ktime_before(txtime, time) || validate_interval) {\n+\t\tif (!ktime_after(txtime, time) || validate_interval) {\n \t\t\ttransmit_end_time = ktime_add_ns(time, packet_transmit_time);\n-\t\t\tif ((ktime_before(curr_intv_start, time) \u0026\u0026\n-\t\t\t ktime_before(transmit_end_time, curr_intv_end)) ||\n+\t\t\tif ((!ktime_after(curr_intv_start, time) \u0026\u0026\n+\t\t\t !ktime_after(transmit_end_time, curr_intv_end)) ||\n \t\t\t (ktime_after(curr_intv_start, time) \u0026\u0026 !validate_interval)) {\n \t\t\t\tentry_found = entry;\n \t\t\t\t*interval_start = curr_intv_start;\n@@ -405,8 +421,19 @@ static bool is_valid_interval(struct sk_buff *skb, struct Qdisc *sch)\n \tstruct sched_entry *entry;\n \n \trcu_read_lock();\n-\tsched = rcu_dereference(q-\u003eoper_sched);\n \tadmin = rcu_dereference(q-\u003eadmin_sched);\n+\tsched = rcu_dereference(q-\u003eoper_sched);\n+\n+\tif (admin \u0026\u0026 !ktime_before(skb-\u003etstamp, admin-\u003ebase_time)) {\n+\t\tsched = admin;\n+\t\tadmin = NULL;\n+\t}\n+\n+\t/* Until the schedule starts, all the queues are open */\n+\tif (!sched || ktime_before(skb-\u003etstamp, sched-\u003ebase_time)) {\n+\t\trcu_read_unlock();\n+\t\treturn true;\n+\t}\n \n \tentry = find_entry_to_transmit(skb, sch, sched, admin, skb-\u003etstamp,\n \t\t\t\t \u0026interval_start, \u0026interval_end, true);\n@@ -484,7 +511,7 @@ static long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)\n \trcu_read_lock();\n \tadmin = rcu_dereference(q-\u003eadmin_sched);\n \tsched = rcu_dereference(q-\u003eoper_sched);\n-\tif (admin \u0026\u0026 ktime_after(minimum_time, admin-\u003ebase_time))\n+\tif (admin \u0026\u0026 !ktime_before(minimum_time, admin-\u003ebase_time))\n \t\tswitch_schedules(q, \u0026admin, \u0026sched);\n \n \t/* Until the schedule starts, all the queues are open */\n@@ -513,8 +540,9 @@ static long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)\n \t\ttxtime = max_t(ktime_t, txtime, interval_start);\n \n \t\tif (admin \u0026\u0026 admin != sched \u0026\u0026\n-\t\t ktime_after(txtime, admin-\u003ebase_time)) {\n+\t\t !ktime_before(txtime, admin-\u003ebase_time)) {\n \t\t\tsched = admin;\n+\t\t\tadmin = NULL;\n \t\t\tsched_changed = true;\n \t\t\tcontinue;\n \t\t}\n@@ -888,7 +916,7 @@ static bool should_change_schedules(const struct sched_gate_list *admin,\n {\n \tktime_t next_base_time, extension_time;\n \n-\tif (!admin)\n+\tif (!admin || !oper)\n \t\treturn false;\n \n \tnext_base_time = sched_base_time(admin);\n@@ -916,6 +944,142 @@ static bool should_change_schedules(const struct sched_gate_list *admin,\n \treturn false;\n }\n \n+static void taprio_set_entry_state(struct taprio_sched *q,\n+\t\t\t\t struct sched_gate_list *oper,\n+\t\t\t\t struct sched_entry *next,\n+\t\t\t\t ktime_t cur_start,\n+\t\t\t\t ktime_t end_time,\n+\t\t\t\t int num_tc)\n+{\n+\tint tc;\n+\n+\tfor (tc = 0; tc \u003c num_tc; tc++) {\n+\t\tif (next-\u003egate_duration[tc] == oper-\u003ecycle_time)\n+\t\t\tnext-\u003egate_close_time[tc] = KTIME_MAX;\n+\t\telse\n+\t\t\tnext-\u003egate_close_time[tc] = ktime_add_ns(cur_start,\n+\t\t\t\t\t\t\t\t next-\u003egate_duration[tc]);\n+\t}\n+\n+\tnext-\u003eend_time = end_time;\n+\ttaprio_set_budgets(q, oper, next);\n+}\n+\n+static void taprio_advance_one_entry(struct taprio_sched *q,\n+\t\t\t\t struct sched_gate_list **p_oper,\n+\t\t\t\t struct sched_gate_list **p_admin,\n+\t\t\t\t struct sched_entry **p_entry,\n+\t\t\t\t struct sched_entry **p_next,\n+\t\t\t\t ktime_t *p_end_time,\n+\t\t\t\t int num_tc)\n+{\n+\tstruct sched_gate_list *oper = *p_oper;\n+\tstruct sched_gate_list *admin = *p_admin;\n+\tstruct sched_entry *entry = *p_entry;\n+\tktime_t cur_start, end_time;\n+\tstruct sched_entry *next;\n+\n+\tif (!oper-\u003ecycle_end_time)\n+\t\toper-\u003ecycle_end_time = ktime_add_ns(sched_base_time(oper),\n+\t\t\t\t\t\t oper-\u003ecycle_time);\n+\n+\tif (should_restart_cycle(oper, entry)) {\n+\t\tnext = list_first_entry(\u0026oper-\u003eentries, struct sched_entry,\n+\t\t\t\t\tlist);\n+\t\toper-\u003ecycle_end_time = ktime_add_ns(oper-\u003ecycle_end_time,\n+\t\t\t\t\t\t oper-\u003ecycle_time);\n+\t} else {\n+\t\tnext = list_next_entry(entry, list);\n+\t}\n+\n+\tcur_start = entry-\u003eend_time;\n+\tend_time = ktime_add_ns(cur_start, next-\u003einterval);\n+\tend_time = min_t(ktime_t, end_time, oper-\u003ecycle_end_time);\n+\n+\tif (should_change_schedules(admin, oper, end_time)) {\n+\t\tswitch_schedules(q, \u0026admin, \u0026oper);\n+\t\tif (!oper-\u003ecycle_end_time)\n+\t\t\toper-\u003ecycle_end_time = ktime_add_ns(sched_base_time(oper),\n+\t\t\t\t\t\t\t oper-\u003ecycle_time);\n+\t\tcur_start = ktime_sub_ns(oper-\u003ecycle_end_time,\n+\t\t\t\t\t oper-\u003ecycle_time);\n+\t\tnext = list_first_entry(\u0026oper-\u003eentries, struct sched_entry, list);\n+\t\tend_time = ktime_add_ns(cur_start, next-\u003einterval);\n+\t\tend_time = min_t(ktime_t, end_time, oper-\u003ecycle_end_time);\n+\t\t*p_admin = admin;\n+\t\t*p_oper = oper;\n+\t}\n+\n+\ttaprio_set_entry_state(q, oper, next, cur_start, end_time, num_tc);\n+\n+\t*p_entry = next;\n+\t*p_next = next;\n+\t*p_end_time = end_time;\n+}\n+\n+static void taprio_fast_forward_sched(struct taprio_sched *q,\n+\t\t\t\t struct sched_gate_list **p_oper,\n+\t\t\t\t struct sched_gate_list **p_admin,\n+\t\t\t\t struct sched_entry **p_next,\n+\t\t\t\t ktime_t *p_end_time,\n+\t\t\t\t ktime_t now,\n+\t\t\t\t int num_tc)\n+{\n+\tstruct sched_gate_list *oper = *p_oper;\n+\tstruct sched_gate_list *admin = *p_admin;\n+\tstruct sched_entry *entry, *next;\n+\tktime_t end_time, cur_start;\n+\ts64 diff;\n+\n+\tif (admin \u0026\u0026 ktime_compare(sched_base_time(admin), now) \u003c= 0)\n+\t\tswitch_schedules(q, \u0026admin, \u0026oper);\n+\n+again:\n+\tif (!oper-\u003ecycle_end_time)\n+\t\toper-\u003ecycle_end_time = ktime_add_ns(sched_base_time(oper),\n+\t\t\t\t\t\t oper-\u003ecycle_time);\n+\n+\tdiff = ktime_sub(now, oper-\u003ecycle_end_time);\n+\tif (diff \u003e= 0) {\n+\t\tu64 cycles = div64_u64(diff, oper-\u003ecycle_time) + 1;\n+\n+\t\toper-\u003ecycle_end_time = ktime_add_ns(oper-\u003ecycle_end_time,\n+\t\t\t\t\t\t cycles * oper-\u003ecycle_time);\n+\t}\n+\n+\tcur_start = ktime_sub_ns(oper-\u003ecycle_end_time, oper-\u003ecycle_time);\n+\tnext = list_last_entry(\u0026oper-\u003eentries, struct sched_entry, list);\n+\tend_time = oper-\u003ecycle_end_time;\n+\n+\tlist_for_each_entry(entry, \u0026oper-\u003eentries, list) {\n+\t\tktime_t cur_end = ktime_add_ns(cur_start, entry-\u003einterval);\n+\n+\t\tif (list_is_last(\u0026entry-\u003elist, \u0026oper-\u003eentries))\n+\t\t\tcur_end = oper-\u003ecycle_end_time;\n+\t\telse\n+\t\t\tcur_end = min_t(ktime_t, cur_end, oper-\u003ecycle_end_time);\n+\n+\t\tif (ktime_compare(cur_end, now) \u003e 0) {\n+\t\t\tnext = entry;\n+\t\t\tend_time = cur_end;\n+\t\t\tbreak;\n+\t\t}\n+\t\tcur_start = cur_end;\n+\t}\n+\n+\tif (should_change_schedules(admin, oper, end_time)) {\n+\t\tswitch_schedules(q, \u0026admin, \u0026oper);\n+\t\tgoto again;\n+\t}\n+\n+\ttaprio_set_entry_state(q, oper, next, cur_start, end_time, num_tc);\n+\n+\t*p_oper = oper;\n+\t*p_admin = admin;\n+\t*p_next = next;\n+\t*p_end_time = end_time;\n+}\n+\n static enum hrtimer_restart advance_sched(struct hrtimer *timer)\n {\n \tstruct taprio_sched *q = container_of(timer, struct taprio_sched,\n@@ -925,8 +1089,7 @@ static enum hrtimer_restart advance_sched(struct hrtimer *timer)\n \tint num_tc = netdev_get_num_tc(dev);\n \tstruct sched_entry *entry, *next;\n \tstruct Qdisc *sch = q-\u003eroot;\n-\tktime_t end_time;\n-\tint tc;\n+\tktime_t end_time, now;\n \n \tspin_lock(\u0026q-\u003ecurrent_entry_lock);\n \tentry = rcu_dereference_protected(q-\u003ecurrent_entry,\n@@ -939,6 +1102,11 @@ static enum hrtimer_restart advance_sched(struct hrtimer *timer)\n \tif (!oper)\n \t\tswitch_schedules(q, \u0026admin, \u0026oper);\n \n+\tif (unlikely(!oper)) {\n+\t\tspin_unlock(\u0026q-\u003ecurrent_entry_lock);\n+\t\treturn HRTIMER_NORESTART;\n+\t}\n+\n \t/* This can happen in two cases: 1. this is the very first run\n \t * of this function (i.e. we weren't running any schedule\n \t * previously); 2. The previous schedule just ended. The first\n@@ -946,49 +1114,61 @@ static enum hrtimer_restart advance_sched(struct hrtimer *timer)\n \t * schedule initialization.\n \t */\n \tif (unlikely(!entry || entry-\u003eend_time == oper-\u003ebase_time)) {\n-\t\tnext = list_first_entry(\u0026oper-\u003eentries, struct sched_entry,\n-\t\t\t\t\tlist);\n-\t\tend_time = next-\u003eend_time;\n-\t\tgoto first_run;\n-\t}\n+\t\tktime_t cur_start;\n \n-\tif (should_restart_cycle(oper, entry)) {\n+\t\tif (!oper-\u003ecycle_end_time)\n+\t\t\toper-\u003ecycle_end_time = ktime_add_ns(sched_base_time(oper),\n+\t\t\t\t\t\t\t oper-\u003ecycle_time);\n+\t\tcur_start = ktime_sub_ns(oper-\u003ecycle_end_time,\n+\t\t\t\t\t oper-\u003ecycle_time);\n \t\tnext = list_first_entry(\u0026oper-\u003eentries, struct sched_entry,\n \t\t\t\t\tlist);\n-\t\toper-\u003ecycle_end_time = ktime_add_ns(oper-\u003ecycle_end_time,\n-\t\t\t\t\t\t oper-\u003ecycle_time);\n+\t\tend_time = ktime_add_ns(cur_start, next-\u003einterval);\n+\t\tend_time = min_t(ktime_t, end_time, oper-\u003ecycle_end_time);\n+\n+\t\tif (should_change_schedules(admin, oper, end_time)) {\n+\t\t\tswitch_schedules(q, \u0026admin, \u0026oper);\n+\t\t\tif (!oper-\u003ecycle_end_time)\n+\t\t\t\toper-\u003ecycle_end_time = ktime_add_ns(sched_base_time(oper),\n+\t\t\t\t\t\t\t\t oper-\u003ecycle_time);\n+\t\t\tcur_start = ktime_sub_ns(oper-\u003ecycle_end_time,\n+\t\t\t\t\t\t oper-\u003ecycle_time);\n+\t\t\tnext = list_first_entry(\u0026oper-\u003eentries,\n+\t\t\t\t\t\tstruct sched_entry, list);\n+\t\t\tend_time = ktime_add_ns(cur_start, next-\u003einterval);\n+\t\t\tend_time = min_t(ktime_t, end_time,\n+\t\t\t\t\t oper-\u003ecycle_end_time);\n+\t\t}\n+\n+\t\ttaprio_set_entry_state(q, oper, next, cur_start, end_time,\n+\t\t\t\t num_tc);\n \t} else {\n-\t\tnext = list_next_entry(entry, list);\n+\t\ttaprio_advance_one_entry(q, \u0026oper, \u0026admin, \u0026entry, \u0026next,\n+\t\t\t\t\t \u0026end_time, num_tc);\n \t}\n \n-\tend_time = ktime_add_ns(entry-\u003eend_time, next-\u003einterval);\n-\tend_time = min_t(ktime_t, end_time, oper-\u003ecycle_end_time);\n-\n-\tfor (tc = 0; tc \u003c num_tc; tc++) {\n-\t\tif (next-\u003egate_duration[tc] == oper-\u003ecycle_time)\n-\t\t\tnext-\u003egate_close_time[tc] = KTIME_MAX;\n-\t\telse\n-\t\t\tnext-\u003egate_close_time[tc] = ktime_add_ns(entry-\u003eend_time,\n-\t\t\t\t\t\t\t\t next-\u003egate_duration[tc]);\n-\t}\n+\tnow = taprio_get_time(q);\n+\tif (unlikely(ktime_compare(end_time, now) \u003c= 0)) {\n+\t\tsize_t max_steps = min_t(size_t, oper-\u003enum_entries, 32);\n+\t\tsize_t i;\n+\n+\t\tfor (i = 0; i \u003c max_steps \u0026\u0026 ktime_compare(end_time, now) \u003c= 0; i++) {\n+\t\t\tentry = next;\n+\t\t\ttaprio_advance_one_entry(q, \u0026oper, \u0026admin, \u0026entry, \u0026next,\n+\t\t\t\t\t\t \u0026end_time, num_tc);\n+\t\t}\n \n-\tif (should_change_schedules(admin, oper, end_time)) {\n-\t\tswitch_schedules(q, \u0026admin, \u0026oper);\n-\t\t/* After changing schedules, the next entry is the first one\n-\t\t * in the new schedule, with a pre-calculated end_time.\n-\t\t */\n-\t\tnext = list_first_entry(\u0026oper-\u003eentries, struct sched_entry, list);\n-\t\tend_time = next-\u003eend_time;\n+\t\tnow = taprio_get_time(q);\n+\t\twhile (ktime_compare(end_time, now) \u003c= 0) {\n+\t\t\ttaprio_fast_forward_sched(q, \u0026oper, \u0026admin, \u0026next,\n+\t\t\t\t\t\t \u0026end_time, now, num_tc);\n+\t\t\tnow = taprio_get_time(q);\n+\t\t}\n \t}\n \n-\tnext-\u003eend_time = end_time;\n-\ttaprio_set_budgets(q, oper, next);\n-\n-first_run:\n \trcu_assign_pointer(q-\u003ecurrent_entry, next);\n-\tspin_unlock(\u0026q-\u003ecurrent_entry_lock);\n-\n \thrtimer_set_expires(\u0026q-\u003eadvance_timer, end_time);\n+\tspin_unlock(\u0026q-\u003ecurrent_entry_lock);\n \n \trcu_read_lock();\n \t__netif_schedule(sch);\n@@ -1130,7 +1310,11 @@ static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,\n \t\t\t\t struct sched_gate_list *new,\n \t\t\t\t struct netlink_ext_ack *extack)\n {\n+\tint min_duration = taprio_min_interval(q);\n+\tstruct sched_entry *entry, *n;\n+\ts64 elapsed = 0;\n \tint err = 0;\n+\tint i = 0;\n \n \tif (tb[TCA_TAPRIO_ATTR_SCHED_SINGLE_ENTRY]) {\n \t\tNL_SET_ERR_MSG(extack, \"Adding a single entry is not supported\");\n@@ -1152,8 +1336,12 @@ static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,\n \tif (err \u003c 0)\n \t\treturn err;\n \n+\tif (list_empty(\u0026new-\u003eentries)) {\n+\t\tNL_SET_ERR_MSG(extack, \"There should be at least one entry in the schedule\");\n+\t\treturn -EINVAL;\n+\t}\n+\n \tif (!new-\u003ecycle_time) {\n-\t\tstruct sched_entry *entry;\n \t\tktime_t cycle = 0;\n \n \t\tlist_for_each_entry(entry, \u0026new-\u003eentries, list)\n@@ -1167,12 +1355,45 @@ static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,\n \t\tnew-\u003ecycle_time = cycle;\n \t}\n \n-\tif (new-\u003ecycle_time \u003c new-\u003enum_entries * length_to_duration(q, ETH_ZLEN)) {\n+\tif (new-\u003ecycle_time \u003c min_duration) {\n \t\tNL_SET_ERR_MSG(extack, \"'cycle_time' is too small\");\n \t\treturn -EINVAL;\n \t}\n \n-\ttaprio_calculate_gate_durations(q, new);\n+\tlist_for_each_entry_safe(entry, n, \u0026new-\u003eentries, list) {\n+\t\tif (elapsed \u003e= new-\u003ecycle_time) {\n+\t\t\tlist_del(\u0026entry-\u003elist);\n+\t\t\tkfree(entry);\n+\t\t\tnew-\u003enum_entries--;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (elapsed + entry-\u003einterval \u003e new-\u003ecycle_time) {\n+\t\t\tentry-\u003einterval = new-\u003ecycle_time - elapsed;\n+\t\t\telapsed = new-\u003ecycle_time;\n+\t\t} else {\n+\t\t\telapsed += entry-\u003einterval;\n+\t\t}\n+\t}\n+\n+\tif (list_empty(\u0026new-\u003eentries)) {\n+\t\tNL_SET_ERR_MSG(extack, \"There should be at least one entry in the schedule\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (elapsed \u003c new-\u003ecycle_time) {\n+\t\tentry = list_last_entry(\u0026new-\u003eentries, struct sched_entry, list);\n+\t\tentry-\u003einterval += new-\u003ecycle_time - elapsed;\n+\t}\n+\n+\tlist_for_each_entry(entry, \u0026new-\u003eentries, list) {\n+\t\tif (entry-\u003einterval \u003c min_duration) {\n+\t\t\tNL_SET_ERR_MSG(extack, \"Invalid interval for schedule entry\");\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tentry-\u003eindex = i++;\n+\t}\n+\tnew-\u003enum_entries = i;\n \n \treturn 0;\n }\n@@ -1256,7 +1477,7 @@ static void setup_first_end_time(struct taprio_sched *q,\n \t/* FIXME: find a better place to do this */\n \tsched-\u003ecycle_end_time = ktime_add_ns(base, cycle);\n \n-\tfirst-\u003eend_time = ktime_add_ns(base, first-\u003einterval);\n+\tfirst-\u003eend_time = ktime_add_ns(base, min_t(s64, first-\u003einterval, cycle));\n \ttaprio_set_budgets(q, sched, first);\n \n \tfor (tc = 0; tc \u003c num_tc; tc++) {\n@@ -1349,11 +1570,11 @@ static int taprio_dev_notifier(struct notifier_block *nb, unsigned long event,\n \t\trcu_read_lock();\n \t\toper = rcu_dereference(q-\u003eoper_sched);\n \t\tif (oper)\n-\t\t\ttaprio_update_queue_max_sdu(q, oper, stab);\n+\t\t\ttaprio_update_queue_max_sdu(q, oper, stab, q-\u003emax_sdu);\n \n \t\tadmin = rcu_dereference(q-\u003eadmin_sched);\n \t\tif (admin)\n-\t\t\ttaprio_update_queue_max_sdu(q, admin, stab);\n+\t\t\ttaprio_update_queue_max_sdu(q, admin, stab, q-\u003emax_sdu);\n \t\trcu_read_unlock();\n \n \t\tbreak;\n@@ -1516,6 +1737,8 @@ static void taprio_cleanup_broken_mqprio(struct taprio_sched *q)\n static int taprio_enable_offload(struct net_device *dev,\n \t\t\t\t struct taprio_sched *q,\n \t\t\t\t struct sched_gate_list *sched,\n+\t\t\t\t const u32 max_sdu[TC_MAX_QUEUE],\n+\t\t\t\t u32 fp[TC_QOPT_MAX_QUEUE],\n \t\t\t\t struct netlink_ext_ack *extack)\n {\n \tconst struct net_device_ops *ops = dev-\u003enetdev_ops;\n@@ -1534,7 +1757,7 @@ static int taprio_enable_offload(struct net_device *dev,\n \n \tif (!caps.supports_queue_max_sdu) {\n \t\tfor (tc = 0; tc \u003c TC_MAX_QUEUE; tc++) {\n-\t\t\tif (q-\u003emax_sdu[tc]) {\n+\t\t\tif (max_sdu[tc]) {\n \t\t\t\tNL_SET_ERR_MSG_MOD(extack,\n \t\t\t\t\t\t \"Device does not handle queueMaxSDU\");\n \t\t\t\treturn -EOPNOTSUPP;\n@@ -1553,10 +1776,10 @@ static int taprio_enable_offload(struct net_device *dev,\n \tmqprio_qopt_reconstruct(dev, \u0026offload-\u003emqprio.qopt);\n \toffload-\u003emqprio.extack = extack;\n \ttaprio_sched_to_offload(dev, sched, offload, \u0026caps);\n-\tmqprio_fp_to_offload(q-\u003efp, \u0026offload-\u003emqprio);\n+\tmqprio_fp_to_offload(fp, \u0026offload-\u003emqprio);\n \n \tfor (tc = 0; tc \u003c TC_MAX_QUEUE; tc++)\n-\t\toffload-\u003emax_sdu[tc] = q-\u003emax_sdu[tc];\n+\t\toffload-\u003emax_sdu[tc] = max_sdu[tc];\n \n \terr = ops-\u003endo_setup_tc(dev, TC_SETUP_QDISC_TAPRIO, offload);\n \tif (err \u003c 0) {\n@@ -1747,14 +1970,14 @@ static int taprio_parse_tc_entry(struct Qdisc *sch,\n \n static int taprio_parse_tc_entries(struct Qdisc *sch,\n \t\t\t\t struct nlattr *opt,\n+\t\t\t\t u32 max_sdu[TC_MAX_QUEUE],\n+\t\t\t\t u32 fp[TC_QOPT_MAX_QUEUE],\n \t\t\t\t struct netlink_ext_ack *extack)\n {\n \tstruct taprio_sched *q = qdisc_priv(sch);\n \tstruct net_device *dev = qdisc_dev(sch);\n-\tu32 max_sdu[TC_QOPT_MAX_QUEUE];\n \tbool have_preemption = false;\n \tunsigned long seen_tcs = 0;\n-\tu32 fp[TC_QOPT_MAX_QUEUE];\n \tstruct nlattr *n;\n \tint tc, rem;\n \tint err = 0;\n@@ -1772,8 +1995,6 @@ static int taprio_parse_tc_entries(struct Qdisc *sch,\n \t}\n \n \tfor (tc = 0; tc \u003c TC_QOPT_MAX_QUEUE; tc++) {\n-\t\tWRITE_ONCE(q-\u003emax_sdu[tc], max_sdu[tc]);\n-\t\tWRITE_ONCE(q-\u003efp[tc], fp[tc]);\n \t\tif (fp[tc] != TC_FP_EXPRESS)\n \t\t\thave_preemption = true;\n \t}\n@@ -1792,7 +2013,7 @@ static int taprio_parse_tc_entries(struct Qdisc *sch,\n \t\t}\n \t}\n \n-\treturn err;\n+\treturn 0;\n }\n \n static int taprio_mqprio_cmp(const struct net_device *dev,\n@@ -1823,15 +2044,33 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,\n \t\t\t struct netlink_ext_ack *extack)\n {\n \tstruct qdisc_size_table *stab = rtnl_dereference(sch-\u003estab);\n+\tstruct sched_gate_list *oper, *admin, *new_admin = NULL;\n+\tstruct netdev_tc_txq old_tc_to_txq[TC_MAX_QUEUE];\n \tstruct nlattr *tb[TCA_TAPRIO_ATTR_MAX + 1] = { };\n-\tstruct sched_gate_list *oper, *admin, *new_admin;\n+\tu8 old_prio_tc_map[TC_BITMASK + 1];\n+\tint old_cur_txq[TC_MAX_QUEUE];\n+\tenum tk_offsets old_tk_offset;\n \tstruct taprio_sched *q = qdisc_priv(sch);\n \tstruct net_device *dev = qdisc_dev(sch);\n \tstruct tc_mqprio_qopt *mqprio = NULL;\n+\tu32 max_sdu[TC_MAX_QUEUE] = { };\n+\tu32 fp[TC_QOPT_MAX_QUEUE] = { };\n+\tbool tc_configured = false;\n+\tint old_clockid, i, err;\n \tunsigned long flags;\n+\tbool initial_setup;\n \tu32 taprio_flags;\n+\ts16 old_num_tc;\n+\tu32 old_flags;\n \tktime_t start;\n-\tint i, err;\n+\n+\toper = rtnl_dereference(q-\u003eoper_sched);\n+\tadmin = rtnl_dereference(q-\u003eadmin_sched);\n+\tinitial_setup = (!oper \u0026\u0026 !admin);\n+\n+\told_flags = q-\u003eflags;\n+\told_clockid = q-\u003eclockid;\n+\told_tk_offset = q-\u003etk_offset;\n \n \terr = nla_parse_nested_deprecated(tb, TCA_TAPRIO_ATTR_MAX, opt,\n \t\t\t\t\t taprio_policy, extack);\n@@ -1866,41 +2105,68 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,\n \t\tWRITE_ONCE(q-\u003eflags, taprio_flags);\n \t}\n \n+\tif (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]) {\n+\t\tif (!TXTIME_ASSIST_IS_ENABLED(q-\u003eflags)) {\n+\t\t\tNL_SET_ERR_MSG_MOD(extack, \"txtime-delay can only be set when txtime-assist mode is enabled\");\n+\t\t\terr = -EINVAL;\n+\t\t\tgoto free_sched;\n+\t\t}\n+\t}\n+\n \t/* Needed for length_to_duration() during netlink attribute parsing */\n \ttaprio_set_picos_per_byte(dev, q, extack);\n \n \terr = taprio_parse_mqprio_opt(dev, mqprio, extack, q-\u003eflags);\n \tif (err \u003c 0)\n-\t\treturn err;\n+\t\tgoto free_sched;\n \n-\terr = taprio_parse_tc_entries(sch, opt, extack);\n+\terr = taprio_parse_tc_entries(sch, opt, max_sdu, fp, extack);\n \tif (err)\n-\t\treturn err;\n+\t\tgoto free_sched;\n \n \tnew_admin = kzalloc_obj(*new_admin);\n \tif (!new_admin) {\n \t\tNL_SET_ERR_MSG(extack, \"Not enough memory for a new schedule\");\n-\t\treturn -ENOMEM;\n+\t\terr = -ENOMEM;\n+\t\tgoto free_sched;\n \t}\n \tINIT_LIST_HEAD(\u0026new_admin-\u003eentries);\n \n-\toper = rtnl_dereference(q-\u003eoper_sched);\n-\tadmin = rtnl_dereference(q-\u003eadmin_sched);\n-\n \t/* no changes - no new mqprio settings */\n \tif (!taprio_mqprio_cmp(dev, mqprio))\n \t\tmqprio = NULL;\n \n-\tif (mqprio \u0026\u0026 (oper || admin)) {\n+\tif (mqprio \u0026\u0026 !initial_setup) {\n \t\tNL_SET_ERR_MSG(extack, \"Changing the traffic mapping of a running schedule is not supported\");\n \t\terr = -ENOTSUPP;\n \t\tgoto free_sched;\n \t}\n \n+\terr = parse_taprio_schedule(q, tb, new_admin, extack);\n+\tif (err \u003c 0)\n+\t\tgoto free_sched;\n+\n+\tif (new_admin-\u003enum_entries == 0) {\n+\t\tNL_SET_ERR_MSG(extack, \"There should be at least one entry in the schedule\");\n+\t\terr = -EINVAL;\n+\t\tgoto free_sched;\n+\t}\n+\n+\terr = taprio_parse_clockid(sch, tb, extack);\n+\tif (err \u003c 0)\n+\t\tgoto free_sched;\n+\n \tif (mqprio) {\n+\t\told_num_tc = dev-\u003enum_tc;\n+\t\tmemcpy(old_tc_to_txq, dev-\u003etc_to_txq, sizeof(old_tc_to_txq));\n+\t\tmemcpy(old_prio_tc_map, dev-\u003eprio_tc_map,\n+\t\t sizeof(old_prio_tc_map));\n+\t\tmemcpy(old_cur_txq, q-\u003ecur_txq, sizeof(old_cur_txq));\n+\n \t\terr = netdev_set_num_tc(dev, mqprio-\u003enum_tc);\n \t\tif (err)\n \t\t\tgoto free_sched;\n+\t\ttc_configured = true;\n \t\tfor (i = 0; i \u003c mqprio-\u003enum_tc; i++) {\n \t\t\tnetdev_set_tc_queue(dev, i,\n \t\t\t\t\t mqprio-\u003ecount[i],\n@@ -1914,24 +2180,12 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,\n \t\t\t\t\t mqprio-\u003eprio_tc_map[i]);\n \t}\n \n-\terr = parse_taprio_schedule(q, tb, new_admin, extack);\n-\tif (err \u003c 0)\n-\t\tgoto free_sched;\n+\ttaprio_calculate_gate_durations(q, new_admin);\n \n-\tif (new_admin-\u003enum_entries == 0) {\n-\t\tNL_SET_ERR_MSG(extack, \"There should be at least one entry in the schedule\");\n-\t\terr = -EINVAL;\n-\t\tgoto free_sched;\n-\t}\n-\n-\terr = taprio_parse_clockid(sch, tb, extack);\n-\tif (err \u003c 0)\n-\t\tgoto free_sched;\n-\n-\ttaprio_update_queue_max_sdu(q, new_admin, stab);\n+\ttaprio_update_queue_max_sdu(q, new_admin, stab, max_sdu);\n \n \tif (FULL_OFFLOAD_IS_ENABLED(q-\u003eflags))\n-\t\terr = taprio_enable_offload(dev, q, new_admin, extack);\n+\t\terr = taprio_enable_offload(dev, q, new_admin, max_sdu, fp, extack);\n \telse\n \t\terr = taprio_disable_offload(dev, q, extack);\n \tif (err)\n@@ -1940,15 +2194,10 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,\n \t/* Protects against enqueue()/dequeue() */\n \tspin_lock_bh(qdisc_lock(sch));\n \n-\tif (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]) {\n-\t\tif (!TXTIME_ASSIST_IS_ENABLED(q-\u003eflags)) {\n-\t\t\tNL_SET_ERR_MSG_MOD(extack, \"txtime-delay can only be set when txtime-assist mode is enabled\");\n-\t\t\terr = -EINVAL;\n-\t\t\tgoto unlock;\n-\t\t}\n-\n-\t\tWRITE_ONCE(q-\u003etxtime_delay,\n-\t\t\t nla_get_u32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]));\n+\terr = taprio_get_start_time(sch, new_admin, \u0026start);\n+\tif (err \u003c 0) {\n+\t\tNL_SET_ERR_MSG(extack, \"Internal error: failed get start time\");\n+\t\tgoto unlock;\n \t}\n \n \tif (!TXTIME_ASSIST_IS_ENABLED(q-\u003eflags) \u0026\u0026\n@@ -1957,11 +2206,14 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,\n \t\thrtimer_setup(\u0026q-\u003eadvance_timer, advance_sched, q-\u003eclockid, HRTIMER_MODE_ABS);\n \t}\n \n-\terr = taprio_get_start_time(sch, new_admin, \u0026start);\n-\tif (err \u003c 0) {\n-\t\tNL_SET_ERR_MSG(extack, \"Internal error: failed get start time\");\n-\t\tgoto unlock;\n-\t}\n+\tif (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY])\n+\t\tWRITE_ONCE(q-\u003etxtime_delay,\n+\t\t\t nla_get_u32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]));\n+\n+\tfor (i = 0; i \u003c TC_MAX_QUEUE; i++)\n+\t\tWRITE_ONCE(q-\u003emax_sdu[i], max_sdu[i]);\n+\tfor (i = 0; i \u003c TC_QOPT_MAX_QUEUE; i++)\n+\t\tWRITE_ONCE(q-\u003efp[i], fp[i]);\n \n \tsetup_txtime(q, new_admin, start);\n \n@@ -2008,6 +2260,22 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,\n \tspin_unlock_bh(qdisc_lock(sch));\n \n free_sched:\n+\tif (err \u0026\u0026 initial_setup) {\n+\t\tif (tc_configured) {\n+\t\t\tWRITE_ONCE(dev-\u003enum_tc, old_num_tc);\n+\t\t\tfor (i = 0; i \u003c TC_MAX_QUEUE; i++)\n+\t\t\t\tWRITE_ONCE(dev-\u003etc_to_txq[i].combined,\n+\t\t\t\t\t old_tc_to_txq[i].combined);\n+\t\t\tfor (i = 0; i \u003c= TC_BITMASK; i++)\n+\t\t\t\tWRITE_ONCE(dev-\u003eprio_tc_map[i],\n+\t\t\t\t\t old_prio_tc_map[i]);\n+\t\t\tmemcpy(q-\u003ecur_txq, old_cur_txq, sizeof(q-\u003ecur_txq));\n+\t\t}\n+\t\tWRITE_ONCE(q-\u003eflags, old_flags);\n+\t\tq-\u003eclockid = old_clockid;\n+\t\tWRITE_ONCE(q-\u003etk_offset, old_tk_offset);\n+\t}\n+\n \tif (new_admin)\n \t\tcall_rcu(\u0026new_admin-\u003ercu, taprio_free_sched_cb);\n \n"
} |
| 2/1 | 2026/10/16 14:48 | agent | patch-evaluator |
2mModel:core Args: null Results: {
"EnableConfigs": [
"NET_SCHED",
"NET_SCH_TAPRIO"
],
"FocusSymbols": [
"advance_sched",
"taprio_fast_forward_sched",
"taprio_advance_one_entry",
"taprio_change",
"parse_taprio_schedule",
"find_entry_to_transmit",
"get_packet_txtime",
"is_valid_interval"
],
"Reasoning": "The patch modifies the TAPRIO (Time-Aware Priority) traffic control queueing discipline (sch_taprio.c) in the networking subsystem. TAPRIO is fully reachable in standard virtualized environments (e.g. using virtual network devices like veth or dummy via rtnetlink). The patch introduces significant logic changes: schedule advancement and fast-forwarding loops in the hrtimer callback (advance_sched, taprio_fast_forward_sched, taprio_advance_one_entry), schedule validation and entry truncation/deletion in parse_taprio_schedule, interval calculations in find_entry_to_transmit and get_packet_txtime, and error-path rollback in taprio_change. These complex timing, looping, and list-handling changes warrant fuzzing to detect regressions, infinite loops, and race conditions.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any '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: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit b51f9aac51b713ad5bb922900aeb8687e60cc7e4
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Oct 16 14:48:53 2026 +0000
syz-cluster: applied patch under review
diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 39ac5b97aa3af..9128f8179a993 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -48,6 +48,10 @@ static struct static_key_false taprio_have_working_mqprio;
* 60 * 17 > PSEC_PER_NSEC (1000)
*/
#define TAPRIO_PICOS_PER_BYTE_MIN 17
+/* Minimum interval for software mode (hrtimer-driven advance_sched) to
+ * avoid hrtimer interrupt storms and CPU starvation.
+ */
+#define TAPRIO_MIN_SW_INTERVAL_NS (10 * NSEC_PER_USEC)
struct sched_entry {
/* Durations between this GCL entry and the GCL entry where the
@@ -259,6 +263,17 @@ static int length_to_duration(struct taprio_sched *q, int len)
return div_u64(len * atomic64_read(&q->picos_per_byte), PSEC_PER_NSEC);
}
+static int taprio_min_interval(struct taprio_sched *q)
+{
+ int min_duration = length_to_duration(q, ETH_ZLEN);
+
+ if (!FULL_OFFLOAD_IS_ENABLED(q->flags) &&
+ !TXTIME_ASSIST_IS_ENABLED(q->flags))
+ min_duration = max_t(int, min_duration, TAPRIO_MIN_SW_INTERVAL_NS);
+
+ return min_duration;
+}
+
static int duration_to_length(struct taprio_sched *q, u64 duration)
{
return div_u64(duration * PSEC_PER_NSEC, atomic64_read(&q->picos_per_byte));
@@ -270,7 +285,8 @@ static int duration_to_length(struct taprio_sched *q, u64 duration)
*/
static void taprio_update_queue_max_sdu(struct taprio_sched *q,
struct sched_gate_list *sched,
- struct qdisc_size_table *stab)
+ struct qdisc_size_table *stab,
+ const u32 max_sdu_user[TC_MAX_QUEUE])
{
struct net_device *dev = qdisc_dev(q->root);
int num_tc = netdev_get_num_tc(dev);
@@ -280,7 +296,7 @@ static void taprio_update_queue_max_sdu(struct taprio_sched *q,
int tc;
for (tc = 0; tc < num_tc; tc++) {
- max_sdu_from_user = q->max_sdu[tc] ?: U32_MAX;
+ max_sdu_from_user = max_sdu_user[tc] ?: U32_MAX;
/* TC gate never closes => keep the queueMaxSDU
* selected by the user
@@ -357,7 +373,7 @@ static struct sched_entry *find_entry_to_transmit(struct sk_buff *skb,
curr_intv_end = get_interval_end_time(sched, admin, entry,
curr_intv_start);
- if (ktime_after(curr_intv_start, cycle_end))
+ if (ktime_compare(curr_intv_start, cycle_end) >= 0)
break;
if (!(entry->gate_mask & BIT(tc)) ||
@@ -366,10 +382,10 @@ static struct sched_entry *find_entry_to_transmit(struct sk_buff *skb,
txtime = entry->next_txtime;
- if (ktime_before(txtime, time) || validate_interval) {
+ if (!ktime_after(txtime, time) || validate_interval) {
transmit_end_time = ktime_add_ns(time, packet_transmit_time);
- if ((ktime_before(curr_intv_start, time) &&
- ktime_before(transmit_end_time, curr_intv_end)) ||
+ if ((!ktime_after(curr_intv_start, time) &&
+ !ktime_after(transmit_end_time, curr_intv_end)) ||
(ktime_after(curr_intv_start, time) && !validate_interval)) {
entry_found = entry;
*interval_start = curr_intv_start;
@@ -405,8 +421,19 @@ static bool is_valid_interval(struct sk_buff *skb, struct Qdisc *sch)
struct sched_entry *entry;
rcu_read_lock();
- sched = rcu_dereference(q->oper_sched);
admin = rcu_dereference(q->admin_sched);
+ sched = rcu_dereference(q->oper_sched);
+
+ if (admin && !ktime_before(skb->tstamp, admin->base_time)) {
+ sched = admin;
+ admin = NULL;
+ }
+
+ /* Until the schedule starts, all the queues are open */
+ if (!sched || ktime_before(skb->tstamp, sched->base_time)) {
+ rcu_read_unlock();
+ return true;
+ }
entry = find_entry_to_transmit(skb, sch, sched, admin, skb->tstamp,
&interval_start, &interval_end, true);
@@ -484,7 +511,7 @@ static long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)
rcu_read_lock();
admin = rcu_dereference(q->admin_sched);
sched = rcu_dereference(q->oper_sched);
- if (admin && ktime_after(minimum_time, admin->base_time))
+ if (admin && !ktime_before(minimum_time, admin->base_time))
switch_schedules(q, &admin, &sched);
/* Until the schedule starts, all the queues are open */
@@ -513,8 +540,9 @@ static long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)
txtime = max_t(ktime_t, txtime, interval_start);
if (admin && admin != sched &&
- ktime_after(txtime, admin->base_time)) {
+ !ktime_before(txtime, admin->base_time)) {
sched = admin;
+ admin = NULL;
sched_changed = true;
continue;
}
@@ -888,7 +916,7 @@ static bool should_change_schedules(const struct sched_gate_list *admin,
{
ktime_t next_base_time, extension_time;
- if (!admin)
+ if (!admin || !oper)
return false;
next_base_time = sched_base_time(admin);
@@ -916,6 +944,142 @@ static bool should_change_schedules(const struct sched_gate_list *admin,
return false;
}
+static void taprio_set_entry_state(struct taprio_sched *q,
+ struct sched_gate_list *oper,
+ struct sched_entry *next,
+ ktime_t cur_start,
+ ktime_t end_time,
+ int num_tc)
+{
+ int tc;
+
+ for (tc = 0; tc < num_tc; tc++) {
+ if (next->gate_duration[tc] == oper->cycle_time)
+ next->gate_close_time[tc] = KTIME_MAX;
+ else
+ next->gate_close_time[tc] = ktime_add_ns(cur_start,
+ next->gate_duration[tc]);
+ }
+
+ next->end_time = end_time;
+ taprio_set_budgets(q, oper, next);
+}
+
+static void taprio_advance_one_entry(struct taprio_sched *q,
+ struct sched_gate_list **p_oper,
+ struct sched_gate_list **p_admin,
+ struct sched_entry **p_entry,
+ struct sched_entry **p_next,
+ ktime_t *p_end_time,
+ int num_tc)
+{
+ struct sched_gate_list *oper = *p_oper;
+ struct sched_gate_list *admin = *p_admin;
+ struct sched_entry *entry = *p_entry;
+ ktime_t cur_start, end_time;
+ struct sched_entry *next;
+
+ if (!oper->cycle_end_time)
+ oper->cycle_end_time = ktime_add_ns(sched_base_time(oper),
+ oper->cycle_time);
+
+ if (should_restart_cycle(oper, entry)) {
+ next = list_first_entry(&oper->entries, struct sched_entry,
+ list);
+ oper->cycle_end_time = ktime_add_ns(oper->cycle_end_time,
+ oper->cycle_time);
+ } else {
+ next = list_next_entry(entry, list);
+ }
+
+ cur_start = entry->end_time;
+ end_time = ktime_add_ns(cur_start, next->interval);
+ end_time = min_t(ktime_t, end_time, oper->cycle_end_time);
+
+ if (should_change_schedules(admin, oper, end_time)) {
+ switch_schedules(q, &admin, &oper);
+ if (!oper->cycle_end_time)
+ oper->cycle_end_time = ktime_add_ns(sched_base_time(oper),
+ oper->cycle_time);
+ cur_start = ktime_sub_ns(oper->cycle_end_time,
+ oper->cycle_time);
+ next = list_first_entry(&oper->entries, struct sched_entry, list);
+ end_time = ktime_add_ns(cur_start, next->interval);
+ end_time = min_t(ktime_t, end_time, oper->cycle_end_time);
+ *p_admin = admin;
+ *p_oper = oper;
+ }
+
+ taprio_set_entry_state(q, oper, next, cur_start, end_time, num_tc);
+
+ *p_entry = next;
+ *p_next = next;
+ *p_end_time = end_time;
+}
+
+static void taprio_fast_forward_sched(struct taprio_sched *q,
+ struct sched_gate_list **p_oper,
+ struct sched_gate_list **p_admin,
+ struct sched_entry **p_next,
+ ktime_t *p_end_time,
+ ktime_t now,
+ int num_tc)
+{
+ struct sched_gate_list *oper = *p_oper;
+ struct sched_gate_list *admin = *p_admin;
+ struct sched_entry *entry, *next;
+ ktime_t end_time, cur_start;
+ s64 diff;
+
+ if (admin && ktime_compare(sched_base_time(admin), now) <= 0)
+ switch_schedules(q, &admin, &oper);
+
+again:
+ if (!oper->cycle_end_time)
+ oper->cycle_end_time = ktime_add_ns(sched_base_time(oper),
+ oper->cycle_time);
+
+ diff = ktime_sub(now, oper->cycle_end_time);
+ if (diff >= 0) {
+ u64 cycles = div64_u64(diff, oper->cycle_time) + 1;
+
+ oper->cycle_end_time = ktime_add_ns(oper->cycle_end_time,
+ cycles * oper->cycle_time);
+ }
+
+ cur_start = ktime_sub_ns(oper->cycle_end_time, oper->cycle_time);
+ next = list_last_entry(&oper->entries, struct sched_entry, list);
+ end_time = oper->cycle_end_time;
+
+ list_for_each_entry(entry, &oper->entries, list) {
+ ktime_t cur_end = ktime_add_ns(cur_start, entry->interval);
+
+ if (list_is_last(&entry->list, &oper->entries))
+ cur_end = oper->cycle_end_time;
+ else
+ cur_end = min_t(ktime_t, cur_end, oper->cycle_end_time);
+
+ if (ktime_compare(cur_end, now) > 0) {
+ next = entry;
+ end_time = cur_end;
+ break;
+ }
+ cur_start = cur_end;
+ }
+
+ if (should_change_schedules(admin, oper, end_time)) {
+ switch_schedules(q, &admin, &oper);
+ goto again;
+ }
+
+ taprio_set_entry_state(q, oper, next, cur_start, end_time, num_tc);
+
+ *p_oper = oper;
+ *p_admin = admin;
+ *p_next = next;
+ *p_end_time = end_time;
+}
+
static enum hrtimer_restart advance_sched(struct hrtimer *timer)
{
struct taprio_sched *q = container_of(timer, struct taprio_sched,
@@ -925,8 +1089,7 @@ static enum hrtimer_restart advance_sched(struct hrtimer *timer)
int num_tc = netdev_get_num_tc(dev);
struct sched_entry *entry, *next;
struct Qdisc *sch = q->root;
- ktime_t end_time;
- int tc;
+ ktime_t end_time, now;
spin_lock(&q->current_entry_lock);
entry = rcu_dereference_protected(q->current_entry,
@@ -939,6 +1102,11 @@ static enum hrtimer_restart advance_sched(struct hrtimer *timer)
if (!oper)
switch_schedules(q, &admin, &oper);
+ if (unlikely(!oper)) {
+ spin_unlock(&q->current_entry_lock);
+ return HRTIMER_NORESTART;
+ }
+
/* This can happen in two cases: 1. this is the very first run
* of this function (i.e. we weren't running any schedule
* previously); 2. The previous schedule just ended. The first
@@ -946,49 +1114,61 @@ static enum hrtimer_restart advance_sched(struct hrtimer *timer)
* schedule initialization.
*/
if (unlikely(!entry || entry->end_time == oper->base_time)) {
- next = list_first_entry(&oper->entries, struct sched_entry,
- list);
- end_time = next->end_time;
- goto first_run;
- }
+ ktime_t cur_start;
- if (should_restart_cycle(oper, entry)) {
+ if (!oper->cycle_end_time)
+ oper->cycle_end_time = ktime_add_ns(sched_base_time(oper),
+ oper->cycle_time);
+ cur_start = ktime_sub_ns(oper->cycle_end_time,
+ oper->cycle_time);
next = list_first_entry(&oper->entries, struct sched_entry,
list);
- oper->cycle_end_time = ktime_add_ns(oper->cycle_end_time,
- oper->cycle_time);
+ end_time = ktime_add_ns(cur_start, next->interval);
+ end_time = min_t(ktime_t, end_time, oper->cycle_end_time);
+
+ if (should_change_schedules(admin, oper, end_time)) {
+ switch_schedules(q, &admin, &oper);
+ if (!oper->cycle_end_time)
+ oper->cycle_end_time = ktime_add_ns(sched_base_time(oper),
+ oper->cycle_time);
+ cur_start = ktime_sub_ns(oper->cycle_end_time,
+ oper->cycle_time);
+ next = list_first_entry(&oper->entries,
+ struct sched_entry, list);
+ end_time = ktime_add_ns(cur_start, next->interval);
+ end_time = min_t(ktime_t, end_time,
+ oper->cycle_end_time);
+ }
+
+ taprio_set_entry_state(q, oper, next, cur_start, end_time,
+ num_tc);
} else {
- next = list_next_entry(entry, list);
+ taprio_advance_one_entry(q, &oper, &admin, &entry, &next,
+ &end_time, num_tc);
}
- end_time = ktime_add_ns(entry->end_time, next->interval);
- end_time = min_t(ktime_t, end_time, oper->cycle_end_time);
-
- for (tc = 0; tc < num_tc; tc++) {
- if (next->gate_duration[tc] == oper->cycle_time)
- next->gate_close_time[tc] = KTIME_MAX;
- else
- next->gate_close_time[tc] = ktime_add_ns(entry->end_time,
- next->gate_duration[tc]);
- }
+ now = taprio_get_time(q);
+ if (unlikely(ktime_compare(end_time, now) <= 0)) {
+ size_t max_steps = min_t(size_t, oper->num_entries, 32);
+ size_t i;
+
+ for (i = 0; i < max_steps && ktime_compare(end_time, now) <= 0; i++) {
+ entry = next;
+ taprio_advance_one_entry(q, &oper, &admin, &entry, &next,
+ &end_time, num_tc);
+ }
- if (should_change_schedules(admin, oper, end_time)) {
- switch_schedules(q, &admin, &oper);
- /* After changing schedules, the next entry is the first one
- * in the new schedule, with a pre-calculated end_time.
- */
- next = list_first_entry(&oper->entries, struct sched_entry, list);
- end_time = next->end_time;
+ now = taprio_get_time(q);
+ while (ktime_compare(end_time, now) <= 0) {
+ taprio_fast_forward_sched(q, &oper, &admin, &next,
+ &end_time, now, num_tc);
+ now = taprio_get_time(q);
+ }
}
- next->end_time = end_time;
- taprio_set_budgets(q, oper, next);
-
-first_run:
rcu_assign_pointer(q->current_entry, next);
- spin_unlock(&q->current_entry_lock);
-
hrtimer_set_expires(&q->advance_timer, end_time);
+ spin_unlock(&q->current_entry_lock);
rcu_read_lock();
__netif_schedule(sch);
@@ -1130,7 +1310,11 @@ static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,
struct sched_gate_list *new,
struct netlink_ext_ack *extack)
{
+ int min_duration = taprio_min_interval(q);
+ struct sched_entry *entry, *n;
+ s64 elapsed = 0;
int err = 0;
+ int i = 0;
if (tb[TCA_TAPRIO_ATTR_SCHED_SINGLE_ENTRY]) {
NL_SET_ERR_MSG(extack, "Adding a single entry is not supported");
@@ -1152,8 +1336,12 @@ static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,
if (err < 0)
return err;
+ if (list_empty(&new->entries)) {
+ NL_SET_ERR_MSG(extack, "There should be at least one entry in the schedule");
+ return -EINVAL;
+ }
+
if (!new->cycle_time) {
- struct sched_entry *entry;
ktime_t cycle = 0;
list_for_each_entry(entry, &new->entries, list)
@@ -1167,12 +1355,45 @@ static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,
new->cycle_time = cycle;
}
- if (new->cycle_time < new->num_entries * length_to_duration(q, ETH_ZLEN)) {
+ if (new->cycle_time < min_duration) {
NL_SET_ERR_MSG(extack, "'cycle_time' is too small");
return -EINVAL;
}
- taprio_calculate_gate_durations(q, new);
+ list_for_each_entry_safe(entry, n, &new->entries, list) {
+ if (elapsed >= new->cycle_time) {
+ list_del(&entry->list);
+ kfree(entry);
+ new->num_entries--;
+ continue;
+ }
+
+ if (elapsed + entry->interval > new->cycle_time) {
+ entry->interval = new->cycle_time - elapsed;
+ elapsed = new->cycle_time;
+ } else {
+ elapsed += entry->interval;
+ }
+ }
+
+ if (list_empty(&new->entries)) {
+ NL_SET_ERR_MSG(extack, "There should be at least one entry in the schedule");
+ return -EINVAL;
+ }
+
+ if (elapsed < new->cycle_time) {
+ entry = list_last_entry(&new->entries, struct sched_entry, list);
+ entry->interval += new->cycle_time - elapsed;
+ }
+
+ list_for_each_entry(entry, &new->entries, list) {
+ if (entry->interval < min_duration) {
+ NL_SET_ERR_MSG(extack, "Invalid interval for schedule entry");
+ return -EINVAL;
+ }
+ entry->index = i++;
+ }
+ new->num_entries = i;
return 0;
}
@@ -1256,7 +1477,7 @@ static void setup_first_end_time(struct taprio_sched *q,
/* FIXME: find a better place to do this */
sched->cycle_end_time = ktime_add_ns(base, cycle);
- first->end_time = ktime_add_ns(base, first->interval);
+ first->end_time = ktime_add_ns(base, min_t(s64, first->interval, cycle));
taprio_set_budgets(q, sched, first);
for (tc = 0; tc < num_tc; tc++) {
@@ -1349,11 +1570,11 @@ static int taprio_dev_notifier(struct notifier_block *nb, unsigned long event,
rcu_read_lock();
oper = rcu_dereference(q->oper_sched);
if (oper)
- taprio_update_queue_max_sdu(q, oper, stab);
+ taprio_update_queue_max_sdu(q, oper, stab, q->max_sdu);
admin = rcu_dereference(q->admin_sched);
if (admin)
- taprio_update_queue_max_sdu(q, admin, stab);
+ taprio_update_queue_max_sdu(q, admin, stab, q->max_sdu);
rcu_read_unlock();
break;
@@ -1516,6 +1737,8 @@ static void taprio_cleanup_broken_mqprio(struct taprio_sched *q)
static int taprio_enable_offload(struct net_device *dev,
struct taprio_sched *q,
struct sched_gate_list *sched,
+ const u32 max_sdu[TC_MAX_QUEUE],
+ u32 fp[TC_QOPT_MAX_QUEUE],
struct netlink_ext_ack *extack)
{
const struct net_device_ops *ops = dev->netdev_ops;
@@ -1534,7 +1757,7 @@ static int taprio_enable_offload(struct net_device *dev,
if (!caps.supports_queue_max_sdu) {
for (tc = 0; tc < TC_MAX_QUEUE; tc++) {
- if (q->max_sdu[tc]) {
+ if (max_sdu[tc]) {
NL_SET_ERR_MSG_MOD(extack,
"Device does not handle queueMaxSDU");
return -EOPNOTSUPP;
@@ -1553,10 +1776,10 @@ static int taprio_enable_offload(struct net_device *dev,
mqprio_qopt_reconstruct(dev, &offload->mqprio.qopt);
offload->mqprio.extack = extack;
taprio_sched_to_offload(dev, sched, offload, &caps);
- mqprio_fp_to_offload(q->fp, &offload->mqprio);
+ mqprio_fp_to_offload(fp, &offload->mqprio);
for (tc = 0; tc < TC_MAX_QUEUE; tc++)
- offload->max_sdu[tc] = q->max_sdu[tc];
+ offload->max_sdu[tc] = max_sdu[tc];
err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TAPRIO, offload);
if (err < 0) {
@@ -1747,14 +1970,14 @@ static int taprio_parse_tc_entry(struct Qdisc *sch,
static int taprio_parse_tc_entries(struct Qdisc *sch,
struct nlattr *opt,
+ u32 max_sdu[TC_MAX_QUEUE],
+ u32 fp[TC_QOPT_MAX_QUEUE],
struct netlink_ext_ack *extack)
{
struct taprio_sched *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);
- u32 max_sdu[TC_QOPT_MAX_QUEUE];
bool have_preemption = false;
unsigned long seen_tcs = 0;
- u32 fp[TC_QOPT_MAX_QUEUE];
struct nlattr *n;
int tc, rem;
int err = 0;
@@ -1772,8 +1995,6 @@ static int taprio_parse_tc_entries(struct Qdisc *sch,
}
for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) {
- WRITE_ONCE(q->max_sdu[tc], max_sdu[tc]);
- WRITE_ONCE(q->fp[tc], fp[tc]);
if (fp[tc] != TC_FP_EXPRESS)
have_preemption = true;
}
@@ -1792,7 +2013,7 @@ static int taprio_parse_tc_entries(struct Qdisc *sch,
}
}
- return err;
+ return 0;
}
static int taprio_mqprio_cmp(const struct net_device *dev,
@@ -1823,15 +2044,33 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
{
struct qdisc_size_table *stab = rtnl_dereference(sch->stab);
+ struct sched_gate_list *oper, *admin, *new_admin = NULL;
+ struct netdev_tc_txq old_tc_to_txq[TC_MAX_QUEUE];
struct nlattr *tb[TCA_TAPRIO_ATTR_MAX + 1] = { };
- struct sched_gate_list *oper, *admin, *new_admin;
+ u8 old_prio_tc_map[TC_BITMASK + 1];
+ int old_cur_txq[TC_MAX_QUEUE];
+ enum tk_offsets old_tk_offset;
struct taprio_sched *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);
struct tc_mqprio_qopt *mqprio = NULL;
+ u32 max_sdu[TC_MAX_QUEUE] = { };
+ u32 fp[TC_QOPT_MAX_QUEUE] = { };
+ bool tc_configured = false;
+ int old_clockid, i, err;
unsigned long flags;
+ bool initial_setup;
u32 taprio_flags;
+ s16 old_num_tc;
+ u32 old_flags;
ktime_t start;
- int i, err;
+
+ oper = rtnl_dereference(q->oper_sched);
+ admin = rtnl_dereference(q->admin_sched);
+ initial_setup = (!oper && !admin);
+
+ old_flags = q->flags;
+ old_clockid = q->clockid;
+ old_tk_offset = q->tk_offset;
err = nla_parse_nested_deprecated(tb, TCA_TAPRIO_ATTR_MAX, opt,
taprio_policy, extack);
@@ -1866,41 +2105,68 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
WRITE_ONCE(q->flags, taprio_flags);
}
+ if (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]) {
+ if (!TXTIME_ASSIST_IS_ENABLED(q->flags)) {
+ NL_SET_ERR_MSG_MOD(extack, "txtime-delay can only be set when txtime-assist mode is enabled");
+ err = -EINVAL;
+ goto free_sched;
+ }
+ }
+
/* Needed for length_to_duration() during netlink attribute parsing */
taprio_set_picos_per_byte(dev, q, extack);
err = taprio_parse_mqprio_opt(dev, mqprio, extack, q->flags);
if (err < 0)
- return err;
+ goto free_sched;
- err = taprio_parse_tc_entries(sch, opt, extack);
+ err = taprio_parse_tc_entries(sch, opt, max_sdu, fp, extack);
if (err)
- return err;
+ goto free_sched;
new_admin = kzalloc_obj(*new_admin);
if (!new_admin) {
NL_SET_ERR_MSG(extack, "Not enough memory for a new schedule");
- return -ENOMEM;
+ err = -ENOMEM;
+ goto free_sched;
}
INIT_LIST_HEAD(&new_admin->entries);
- oper = rtnl_dereference(q->oper_sched);
- admin = rtnl_dereference(q->admin_sched);
-
/* no changes - no new mqprio settings */
if (!taprio_mqprio_cmp(dev, mqprio))
mqprio = NULL;
- if (mqprio && (oper || admin)) {
+ if (mqprio && !initial_setup) {
NL_SET_ERR_MSG(extack, "Changing the traffic mapping of a running schedule is not supported");
err = -ENOTSUPP;
goto free_sched;
}
+ err = parse_taprio_schedule(q, tb, new_admin, extack);
+ if (err < 0)
+ goto free_sched;
+
+ if (new_admin->num_entries == 0) {
+ NL_SET_ERR_MSG(extack, "There should be at least one entry in the schedule");
+ err = -EINVAL;
+ goto free_sched;
+ }
+
+ err = taprio_parse_clockid(sch, tb, extack);
+ if (err < 0)
+ goto free_sched;
+
if (mqprio) {
+ old_num_tc = dev->num_tc;
+ memcpy(old_tc_to_txq, dev->tc_to_txq, sizeof(old_tc_to_txq));
+ memcpy(old_prio_tc_map, dev->prio_tc_map,
+ sizeof(old_prio_tc_map));
+ memcpy(old_cur_txq, q->cur_txq, sizeof(old_cur_txq));
+
err = netdev_set_num_tc(dev, mqprio->num_tc);
if (err)
goto free_sched;
+ tc_configured = true;
for (i = 0; i < mqprio->num_tc; i++) {
netdev_set_tc_queue(dev, i,
mqprio->count[i],
@@ -1914,24 +2180,12 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
mqprio->prio_tc_map[i]);
}
- err = parse_taprio_schedule(q, tb, new_admin, extack);
- if (err < 0)
- goto free_sched;
+ taprio_calculate_gate_durations(q, new_admin);
- if (new_admin->num_entries == 0) {
- NL_SET_ERR_MSG(extack, "There should be at least one entry in the schedule");
- err = -EINVAL;
- goto free_sched;
- }
-
- err = taprio_parse_clockid(sch, tb, extack);
- if (err < 0)
- goto free_sched;
-
- taprio_update_queue_max_sdu(q, new_admin, stab);
+ taprio_update_queue_max_sdu(q, new_admin, stab, max_sdu);
if (FULL_OFFLOAD_IS_ENABLED(q->flags))
- err = taprio_enable_offload(dev, q, new_admin, extack);
+ err = taprio_enable_offload(dev, q, new_admin, max_sdu, fp, extack);
else
err = taprio_disable_offload(dev, q, extack);
if (err)
@@ -1940,15 +2194,10 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
/* Protects against enqueue()/dequeue() */
spin_lock_bh(qdisc_lock(sch));
- if (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]) {
- if (!TXTIME_ASSIST_IS_ENABLED(q->flags)) {
- NL_SET_ERR_MSG_MOD(extack, "txtime-delay can only be set when txtime-assist mode is enabled");
- err = -EINVAL;
- goto unlock;
- }
-
- WRITE_ONCE(q->txtime_delay,
- nla_get_u32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]));
+ err = taprio_get_start_time(sch, new_admin, &start);
+ if (err < 0) {
+ NL_SET_ERR_MSG(extack, "Internal error: failed get start time");
+ goto unlock;
}
if (!TXTIME_ASSIST_IS_ENABLED(q->flags) &&
@@ -1957,11 +2206,14 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
hrtimer_setup(&q->advance_timer, advance_sched, q->clockid, HRTIMER_MODE_ABS);
}
- err = taprio_get_start_time(sch, new_admin, &start);
- if (err < 0) {
- NL_SET_ERR_MSG(extack, "Internal error: failed get start time");
- goto unlock;
- }
+ if (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY])
+ WRITE_ONCE(q->txtime_delay,
+ nla_get_u32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]));
+
+ for (i = 0; i < TC_MAX_QUEUE; i++)
+ WRITE_ONCE(q->max_sdu[i], max_sdu[i]);
+ for (i = 0; i < TC_QOPT_MAX_QUEUE; i++)
+ WRITE_ONCE(q->fp[i], fp[i]);
setup_txtime(q, new_admin, start);
@@ -2008,6 +2260,22 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
spin_unlock_bh(qdisc_lock(sch));
free_sched:
+ if (err && initial_setup) {
+ if (tc_configured) {
+ WRITE_ONCE(dev->num_tc, old_num_tc);
+ for (i = 0; i < TC_MAX_QUEUE; i++)
+ WRITE_ONCE(dev->tc_to_txq[i].combined,
+ old_tc_to_txq[i].combined);
+ for (i = 0; i <= TC_BITMASK; i++)
+ WRITE_ONCE(dev->prio_tc_map[i],
+ old_prio_tc_map[i]);
+ memcpy(q->cur_txq, old_cur_txq, sizeof(q->cur_txq));
+ }
+ WRITE_ONCE(q->flags, old_flags);
+ q->clockid = old_clockid;
+ WRITE_ONCE(q->tk_offset, old_tk_offset);
+ }
+
if (new_admin)
call_rcu(&new_admin->rcu, taprio_free_sched_cb);
|
| 3/2 | 2026/10/16 14:48 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 13122 output: 32 thoughts: 246 Thoughts: **Evaluating the TAPRIO Scheduler Patch** Okay, so I need to evaluate this patch series affecting the `sch_taprio.c` file in the Linux kernel on amd64. First, let's get a handle on what's changed. I need to understand the context here – what is `sch_taprio`? Ah, right, that's the Time-Aware Priority (TAPRIO) scheduler within the network traffic control subsystem. I'm already thinking about potential vulnerabilities. Now, a key question: is this even relevant in virtualized environments? It's crucial for understanding how applicable any fuzzing efforts would be. Of course, standard qdiscs, including TAPRIO, are attachable to virtual devices via netlink, which means this *is* a relevant target. Syzkaller, in particular, frequently targets TC qdiscs, so it's probably worth investigating. Next steps? Time to dig into the patch itself. I'll need to examine the changes meticulously. I want to determine if it's worth fuzzing and, if so, what kernel configurations are needed to enable the code paths touched by the patch. Finally, I need to identify which symbols within the modified code might be good focus points for the fuzzing. To do this, I'll need to use tools like `grepper` or `read-file` to check `net/sched/sch_taprio.c` and also `net/sched/Kconfig` to understand the dependencies and configuration options. |
| 4/2 | 2026/10/16 14:49 | tool | grepper |
0mArgs:{
"Expression": "config NET_SCH_TAPRIO",
"PathPrefixes": [
"net/sched/"
]
}Results: {
"Output": "net/sched/Kconfig=167=config NET_SCH_MQPRIO_LIB\n--\nnet/sched/Kconfig-171-\nnet/sched/Kconfig:172:config NET_SCH_TAPRIO\nnet/sched/Kconfig-173-\ttristate \"Time Aware Priority (taprio) Scheduler\"\n"
} |
| 5/2 | 2026/10/16 14:49 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 13811 output: 34 thoughts: 22 Thoughts: Okay, here's the summary, tailored for an expert audience, in the first person, reflecting the specified thought process: **Diving into `net/sched/Kconfig` for Dependencies** Right, let's get down to brass tacks. I need to understand the dependencies for this specific network scheduling configuration. A quick check of `net/sched/Kconfig` around line 170 should be the most direct way to get this information. I'll need to see what other features or modules are required or mutually exclusive to this scheduler. Understanding those dependencies is key to confirming integration with the system and its architecture. Let's see what we can dig up. |
| 6/2 | 2026/10/16 14:49 | tool | read-file |
0mArgs:{
"File": "net/sched/Kconfig",
"FirstLine": 165,
"LineCount": 30
}Results: {
"Contents": " 165:\t\t module will be called sch_etf.\n 166:\t\n 167:\tconfig NET_SCH_MQPRIO_LIB\n 168:\t\ttristate\n 169:\t\thelp\n 170:\t\t Common library for manipulating mqprio queue configurations.\n 171:\t\n 172:\tconfig NET_SCH_TAPRIO\n 173:\t\ttristate \"Time Aware Priority (taprio) Scheduler\"\n 174:\t\tselect NET_SCH_MQPRIO_LIB\n 175:\t\thelp\n 176:\t\t Say Y here if you want to use the Time Aware Priority (taprio) packet\n 177:\t\t scheduling algorithm.\n 178:\t\n 179:\t\t See the top of \u003cfile:net/sched/sch_taprio.c\u003e for more details.\n 180:\t\n 181:\t\t To compile this code as a module, choose M here: the\n 182:\t\t module will be called sch_taprio.\n 183:\t\n 184:\tconfig NET_SCH_GRED\n 185:\t\ttristate \"Generic Random Early Detection (GRED)\"\n 186:\t\thelp\n 187:\t\t Say Y here if you want to use the Generic Random Early Detection\n 188:\t\t (GRED) packet scheduling algorithm for some of your network devices\n 189:\t\t (see the top of \u003cfile:net/sched/sch_red.c\u003e for details and\n 190:\t\t references about the algorithm).\n 191:\t\n 192:\t\t To compile this code as a module, choose M here: the\n 193:\t\t module will be called sch_gred.\n 194:\t\n"
} |
| 7/2 | 2026/10/16 14:49 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 14467 output: 32 thoughts: 29 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Investigating `NET_SCH_TAPRIO` Dependencies** Alright, let's dive in. My immediate thought is to understand the dependencies of `NET_SCH_TAPRIO`. I need to figure out if it relies on any other kernel configurations. The logical first step is to crack open `net/sched/Kconfig` and take a look at the top-level definitions. No point in wasting time if it's got some obvious hard requirements that I need to be aware of. Let's see if there are any prerequisites I need to consider for enabling it, any supporting features it leverages, or any alternative schedulers it might conflict with. I'm especially keen on any implicit dependencies that might not be immediately obvious in the code, stuff that might show up in error logs later if overlooked. Let's get this dependency picture crystal clear before I go any further. |
| 8/2 | 2026/10/16 14:49 | tool | read-file |
0mArgs:{
"File": "net/sched/Kconfig",
"FirstLine": 1,
"LineCount": 40
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\t#\n 3:\t# Traffic control configuration.\n 4:\t#\n 5:\t\n 6:\tmenuconfig NET_SCHED\n 7:\t\tbool \"QoS and/or fair queueing\"\n 8:\t\tselect NET_SCH_FIFO\n 9:\t\thelp\n 10:\t\t When the kernel has several packets to send out over a network\n 11:\t\t device, it has to decide which ones to send first, which ones to\n 12:\t\t delay, and which ones to drop. This is the job of the queueing\n 13:\t\t disciplines, several different algorithms for how to do this\n 14:\t\t \"fairly\" have been proposed.\n 15:\t\n 16:\t\t If you say N here, you will get the standard packet scheduler, which\n 17:\t\t is a FIFO (first come, first served). If you say Y here, you will be\n 18:\t\t able to choose from among several alternative algorithms which can\n 19:\t\t then be attached to different network devices. This is useful for\n 20:\t\t example if some of your network devices are real time devices that\n 21:\t\t need a certain minimum data flow rate, or if you need to limit the\n 22:\t\t maximum data flow rate for traffic which matches specified criteria.\n 23:\t\t This code is considered to be experimental.\n 24:\t\n 25:\t\t To administer these schedulers, you'll need the user-level utilities\n 26:\t\t from the package iproute2+tc at\n 27:\t\t \u003chttps://www.kernel.org/pub/linux/utils/net/iproute2/\u003e. That package\n 28:\t\t also contains some documentation; for more, check out\n 29:\t\t \u003chttp://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2\u003e.\n 30:\t\n 31:\t\t This Quality of Service (QoS) support will enable you to use\n 32:\t\t Differentiated Services (diffserv) and Resource Reservation Protocol\n 33:\t\t (RSVP) on your Linux router if you also say Y to the corresponding\n 34:\t\t classifiers below. Documentation and software is at\n 35:\t\t \u003chttp://diffserv.sourceforge.net/\u003e.\n 36:\t\n 37:\t\t If you say Y here and to \"/proc file system\" below, you will be able\n 38:\t\t to read status information about packet schedulers from the file\n 39:\t\t /proc/net/psched.\n 40:\t\n"
} |
| 9/2 | 2026/10/16 14:49 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 15386 output: 38 thoughts: 1078 Thoughts: **TAPRIO Code Review & Reachability Assessment** Okay, so I see that `NET_SCH_TAPRIO` depends on `NET_SCHED` – makes sense, standard dependency. Now, let's dive into this patch. I need to understand what's changed and how. Looking at the diff, it's pretty extensive. First, there's a new constant, `TAPRIO_MIN_SW_INTERVAL_NS`, setting a minimum software interval. The `taprio_min_interval` function will enforce this, calculating and setting a minimum. There's a subtle but interesting change in `taprio_update_queue_max_sdu`; it now receives `max_sdu_user` instead of directly accessing `q->max_sdu`. That's likely for better control over the source of the `max_sdu`. The `find_entry_to_transmit` function has some revised logic: switching `ktime_after` with `ktime_compare` for better accuracy, new checks involving `validate_interval`. The added checks in `is_valid_interval` handle admin schedule usage. `get_packet_txtime` updates admin scheduling, and `should_change_schedules` has a simple return if admin or oper are invalid. `taprio_set_entry_state` seems to be responsible for setting the gate close and end times, and it calls `taprio_set_budgets`. `taprio_advance_one_entry` advances a single entry in the schedule, while `taprio_fast_forward_sched` deals with catching up the schedule when the timer has expired significantly in the past, likely to handle missed timer events. It has a `goto` statement for a loop there. The `advance_sched` hrtimer callback has undergone a significant rework, incorporating `taprio_advance_one_entry` and `taprio_fast_forward_sched` for first run and missed timer logic. `parse_taprio_schedule` now validates min_duration and does some serious schedule parsing. It has a check for elapsed time exceeding cycle time, with logic for truncation and deletion of extra entries, and validation of intervals. `setup_first_end_time` sets the first end time. Finally, `taprio_change` has added error handling and state rollback during TC configuration failure; it passes data locally instead of prematurely modifying `q`. This patch represents a major refactoring of the scheduling logic in `sch_taprio.c`, specifically: - Complex scheduling logic in `advance_sched`, `taprio_fast_forward_sched`, `taprio_advance_one_entry` - Schedule parsing and truncation in `parse_taprio_schedule` - Error unwinding and state rollback in `taprio_change` - Packet TX time and gate interval calculations in `find_entry_to_transmit`, `is_valid_interval`, `get_packet_txtime` The big question now is reachability. Can this be triggered reliably? I'm betting it's fully reachable. The TAPRIO qdisc should be accessible within a standard QEMU environment. A user program (or syzkaller) should be able to create a virtual device (tap, veth, etc.) configure a taprio qdisc via netlink, send packets, set up and change schedules, and trigger timer expirations and fast forward. Now, let's check syzkaller. I need to see if there are already descriptions for TAPRIO, or if it is fuzzed at all. And then I will look at the callers and entry points of `net/sched/sch_taprio.c`. |
| 10/2 | 2026/10/16 14:50 | tool | grepper |
0mArgs:{
"Expression": "taprio_qdisc_ops",
"PathPrefixes": [
"net/sched/sch_taprio.c"
]
}Results: {
"Output": "net/sched/sch_taprio.c=2804=static const struct Qdisc_class_ops taprio_class_ops = {\n--\nnet/sched/sch_taprio.c-2813-\nnet/sched/sch_taprio.c:2814:static struct Qdisc_ops taprio_qdisc_ops __read_mostly = {\nnet/sched/sch_taprio.c-2815-\t.cl_ops\t\t= \u0026taprio_class_ops,\n--\nnet/sched/sch_taprio.c=2836=static int __init taprio_module_init(void)\n--\nnet/sched/sch_taprio.c-2842-\nnet/sched/sch_taprio.c:2843:\treturn register_qdisc(\u0026taprio_qdisc_ops);\nnet/sched/sch_taprio.c-2844-}\n--\nnet/sched/sch_taprio.c=2846=static void __exit taprio_module_exit(void)\nnet/sched/sch_taprio.c-2847-{\nnet/sched/sch_taprio.c:2848:\tunregister_qdisc(\u0026taprio_qdisc_ops);\nnet/sched/sch_taprio.c-2849-\tunregister_netdevice_notifier(\u0026taprio_device_notifier);\n"
} |
| 11/2 | 2026/10/16 14:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 17632 output: 39 thoughts: 15 Thoughts: Okay, let's dive into it. **My Analysis of `taprio_qdisc_ops`** Right, so I need to get a handle on `taprio_qdisc_ops`. Let me pull up the definition. I'm already pretty familiar with Qdisc operations, but I want to refresh my memory and pinpoint the specific hooks and structures relevant to this `taprio` implementation. I know these `ops` structs are crucial – they define the entry points for all the key functions the scheduler uses. I need to understand how the `taprio` scheduler is actually *doing* things. Let's see what's in there... It should give me a good framework to then check the implementation details. |
| 12/2 | 2026/10/16 14:50 | tool | read-file |
0mArgs:{
"File": "net/sched/sch_taprio.c",
"FirstLine": 2810,
"LineCount": 35
}Results: {
"Contents": "2810:\t\t.dump_stats\t= taprio_dump_class_stats,\n2811:\t\t.select_queue\t= taprio_select_queue,\n2812:\t};\n2813:\t\n2814:\tstatic struct Qdisc_ops taprio_qdisc_ops __read_mostly = {\n2815:\t\t.cl_ops\t\t= \u0026taprio_class_ops,\n2816:\t\t.id\t\t= \"taprio\",\n2817:\t\t.priv_size\t= sizeof(struct taprio_sched),\n2818:\t\t.init\t\t= taprio_init,\n2819:\t\t.change\t\t= taprio_change,\n2820:\t\t.destroy\t= taprio_destroy,\n2821:\t\t.reset\t\t= taprio_reset,\n2822:\t\t.attach\t\t= taprio_attach,\n2823:\t\t.peek\t\t= taprio_peek,\n2824:\t\t.dequeue\t= taprio_dequeue,\n2825:\t\t.enqueue\t= taprio_enqueue,\n2826:\t\t.dump\t\t= taprio_dump,\n2827:\t\t.dump_stats\t= taprio_dump_stats,\n2828:\t\t.owner\t\t= THIS_MODULE,\n2829:\t};\n2830:\tMODULE_ALIAS_NET_SCH(\"taprio\");\n2831:\t\n2832:\tstatic struct notifier_block taprio_device_notifier = {\n2833:\t\t.notifier_call = taprio_dev_notifier,\n2834:\t};\n2835:\t\n2836:\tstatic int __init taprio_module_init(void)\n2837:\t{\n2838:\t\tint err = register_netdevice_notifier(\u0026taprio_device_notifier);\n2839:\t\n2840:\t\tif (err)\n2841:\t\t\treturn err;\n2842:\t\n2843:\t\treturn register_qdisc(\u0026taprio_qdisc_ops);\n2844:\t}\n"
} |
| 13/2 | 2026/10/16 14:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 18373 output: 51 thoughts: 425 Thoughts: **Analyzing TAPRIO Scheduler Function Inlining and Visibility** Okay, let's break down these TAPRIO scheduler functions and figure out their potential visibility in traces and coverage data. I need to understand how the changes I'm looking at impact the code's observability. First, I see `advance_sched`, `taprio_fast_forward_sched`, `taprio_advance_one_entry`, `taprio_change`, `taprio_init`, `parse_taprio_schedule`, `find_entry_to_transmit`, `taprio_enqueue`, `taprio_dequeue`, `is_valid_interval`, and `get_packet_txtime`. I'm focused on what's been modified, specifically in `net/sched/sch_taprio.c`. I'm going through the list and noting the function attributes. `advance_sched` is a `static enum hrtimer_restart advance_sched(struct hrtimer *timer)`. That's an hrtimer callback for `q->advance_timer`. Since it's a callback, it *should* show up nicely in stack traces and coverage, unless it's been completely inlined into some wrapper function – but hrtimer callbacks are usually function pointers, so it should be visible. Good. `taprio_fast_forward_sched` and `taprio_advance_one_entry` are also static void functions, as is `parse_taprio_schedule`. The `static` keyword means they are only visible within the file. This *might* complicate tracing, but it won't be a showstopper. `taprio_change` is a `static int taprio_change(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack)`. This is the Qdisc_ops' .change method. Because it's a Qdisc operation, it should be pretty visible in the tracing. `taprio_init` is called from `taprio_change`. I'm making a note to check the callers of `find_entry_to_transmit`, `is_valid_interval`, and `get_packet_txtime`. This is where I'll get more insight to how they may have been inlined, or visible. |
| 14/2 | 2026/10/16 14:50 | tool | grepper |
0mArgs:{
"Expression": "find_entry_to_transmit|is_valid_interval|get_packet_txtime",
"PathPrefixes": [
"net/sched/sch_taprio.c"
]
}Results: {
"Output": "net/sched/sch_taprio.c=286=static void taprio_update_queue_max_sdu(struct taprio_sched *q,\n--\nnet/sched/sch_taprio.c-338- */\nnet/sched/sch_taprio.c:339:static struct sched_entry *find_entry_to_transmit(struct sk_buff *skb,\nnet/sched/sch_taprio.c-340-\t\t\t\t\t\t struct Qdisc *sch,\n--\nnet/sched/sch_taprio.c-415-\nnet/sched/sch_taprio.c:416:static bool is_valid_interval(struct sk_buff *skb, struct Qdisc *sch)\nnet/sched/sch_taprio.c-417-{\n--\nnet/sched/sch_taprio.c-437-\nnet/sched/sch_taprio.c:438:\tentry = find_entry_to_transmit(skb, sch, sched, admin, skb-\u003etstamp,\nnet/sched/sch_taprio.c-439-\t\t\t\t \u0026interval_start, \u0026interval_end, true);\n--\nnet/sched/sch_taprio.c=446=static ktime_t get_tcp_tstamp(struct taprio_sched *q, struct sk_buff *skb)\n--\nnet/sched/sch_taprio.c-494- */\nnet/sched/sch_taprio.c:495:static long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)\nnet/sched/sch_taprio.c-496-{\n--\nnet/sched/sch_taprio.c-528-\nnet/sched/sch_taprio.c:529:\t\tentry = find_entry_to_transmit(skb, sch, sched, admin,\nnet/sched/sch_taprio.c-530-\t\t\t\t\t minimum_time,\n--\nnet/sched/sch_taprio.c=589=static int taprio_enqueue_one(struct sk_buff *skb, struct Qdisc *sch,\n--\nnet/sched/sch_taprio.c-595-\tif (skb-\u003esk \u0026\u0026 sk_fullsock(skb-\u003esk) \u0026\u0026 sock_flag(skb-\u003esk, SOCK_TXTIME)) {\nnet/sched/sch_taprio.c:596:\t\tif (!is_valid_interval(skb, sch))\nnet/sched/sch_taprio.c-597-\t\t\treturn qdisc_drop(skb, sch, to_free);\nnet/sched/sch_taprio.c-598-\t} else if (TXTIME_ASSIST_IS_ENABLED(q-\u003eflags)) {\nnet/sched/sch_taprio.c:599:\t\tskb-\u003etstamp = get_packet_txtime(skb, sch);\nnet/sched/sch_taprio.c-600-\t\tif (!skb-\u003etstamp)\n"
} |
| 15/2 | 2026/10/16 14:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 20080 output: 35 thoughts: 13 Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person: **Initial Thoughts on `taprio_init`** Alright, let's dive into the `taprio_init` function. I need to understand what's happening at the very beginning of the process. I'm especially interested in the core setup and initialization phases. Are we talking about allocating data structures, setting up inter-thread communication, loading configuration files, or something else entirely? The name suggests a "start-up" or "initiation" phase, but it's important to know the specific context within the larger system. Are we dealing with real-time scheduling constraints, resource allocation for tasks, or initial calibration/synchronization procedures? My intuition leans toward the system starting to create the objects it is going to use, but what is the full extent of this? |
| 16/2 | 2026/10/16 14:50 | tool | grepper |
0mArgs:{
"Expression": "taprio_init",
"PathPrefixes": [
"net/sched/sch_taprio.c"
]
}Results: {
"Output": "net/sched/sch_taprio.c=2300=static void taprio_destroy(struct Qdisc *sch)\n--\nnet/sched/sch_taprio.c-2309-\t/* Note that taprio_reset() might not be called if an error\nnet/sched/sch_taprio.c:2310:\t * happens in qdisc_create(), after taprio_init() has been called.\nnet/sched/sch_taprio.c-2311-\t */\n--\nnet/sched/sch_taprio.c-2338-\nnet/sched/sch_taprio.c:2339:static int taprio_init(struct Qdisc *sch, struct nlattr *opt,\nnet/sched/sch_taprio.c-2340-\t\t struct netlink_ext_ack *extack)\n--\nnet/sched/sch_taprio.c=2814=static struct Qdisc_ops taprio_qdisc_ops __read_mostly = {\n--\nnet/sched/sch_taprio.c-2817-\t.priv_size\t= sizeof(struct taprio_sched),\nnet/sched/sch_taprio.c:2818:\t.init\t\t= taprio_init,\nnet/sched/sch_taprio.c-2819-\t.change\t\t= taprio_change,\n"
} |
| 17/2 | 2026/10/16 14:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 20660 output: 39 thoughts: 22 Thoughts: Here's a summary of my thought process: **Diving into `taprio_init` for a Deep Dive** Okay, time to get to the heart of the matter. I need to take a look at the `taprio_init` function, specifically lines 2339 through 2365. This section likely contains critical logic related to initialization – understanding the context here could be key to whatever I'm troubleshooting. Let's see what gems are hidden in this snippet. Hopefully, it's well-commented and easy to parse, because I'm ready to dive into some complex logic. Let's get to it! |
| 18/2 | 2026/10/16 14:50 | tool | read-file |
0mArgs:{
"File": "net/sched/sch_taprio.c",
"FirstLine": 2339,
"LineCount": 35
}Results: {
"Contents": "2339:\tstatic int taprio_init(struct Qdisc *sch, struct nlattr *opt,\n2340:\t\t\t struct netlink_ext_ack *extack)\n2341:\t{\n2342:\t\tstruct taprio_sched *q = qdisc_priv(sch);\n2343:\t\tstruct net_device *dev = qdisc_dev(sch);\n2344:\t\tint i, tc;\n2345:\t\n2346:\t\tspin_lock_init(\u0026q-\u003ecurrent_entry_lock);\n2347:\t\n2348:\t\thrtimer_setup(\u0026q-\u003eadvance_timer, advance_sched, CLOCK_TAI, HRTIMER_MODE_ABS);\n2349:\t\n2350:\t\tq-\u003eroot = sch;\n2351:\t\n2352:\t\t/* We only support static clockids. Use an invalid value as default\n2353:\t\t * and get the valid one on taprio_change().\n2354:\t\t */\n2355:\t\tq-\u003eclockid = -1;\n2356:\t\tq-\u003eflags = TAPRIO_FLAGS_INVALID;\n2357:\t\n2358:\t\tlist_add(\u0026q-\u003etaprio_list, \u0026taprio_list);\n2359:\t\n2360:\t\tif (sch-\u003eparent != TC_H_ROOT) {\n2361:\t\t\tNL_SET_ERR_MSG_MOD(extack, \"Can only be attached as root qdisc\");\n2362:\t\t\treturn -EOPNOTSUPP;\n2363:\t\t}\n2364:\t\n2365:\t\tif (!netif_is_multiqueue(dev)) {\n2366:\t\t\tNL_SET_ERR_MSG_MOD(extack, \"Multi-queue device is required\");\n2367:\t\t\treturn -EOPNOTSUPP;\n2368:\t\t}\n2369:\t\n2370:\t\tq-\u003eqdiscs = kzalloc_objs(q-\u003eqdiscs[0], dev-\u003enum_tx_queues);\n2371:\t\tif (!q-\u003eqdiscs)\n2372:\t\t\treturn -ENOMEM;\n2373:\t\n"
} |
| 19/2 | 2026/10/16 14:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 21426 output: 39 thoughts: 20 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into `taprio_init` After Line 2373** Okay, I'm zeroing in on `taprio_init` after that specific line - 2373. I need to understand what's happening at this point in the initialization sequence. I'm assuming, based on my familiarity with the code base, that line 2373 probably marks a significant transition or the setup of a specific module within TAPRIO. My goal is to grasp the data structures being manipulated, the function calls being made, and any potential side effects or state changes that become relevant from this point forward. I need to be ready to trace and understand any dependencies and downstream consequences that are created as `taprio_init` unfolds. I'm anticipating that some key configurations must have been loaded by this line, so I'll be particularly watchful for where and how that loaded config is being implemented. I'm focusing on the implementation details after line 2373 because I believe this is where the real interesting details of the initialization are. |
| 20/2 | 2026/10/16 14:50 | tool | read-file |
0mArgs:{
"File": "net/sched/sch_taprio.c",
"FirstLine": 2370,
"LineCount": 40
}Results: {
"Contents": "2370:\t\tq-\u003eqdiscs = kzalloc_objs(q-\u003eqdiscs[0], dev-\u003enum_tx_queues);\n2371:\t\tif (!q-\u003eqdiscs)\n2372:\t\t\treturn -ENOMEM;\n2373:\t\n2374:\t\tif (!opt)\n2375:\t\t\treturn -EINVAL;\n2376:\t\n2377:\t\tfor (i = 0; i \u003c dev-\u003enum_tx_queues; i++) {\n2378:\t\t\tstruct netdev_queue *dev_queue;\n2379:\t\t\tstruct Qdisc *qdisc;\n2380:\t\n2381:\t\t\tdev_queue = netdev_get_tx_queue(dev, i);\n2382:\t\t\tqdisc = qdisc_create_dflt(dev_queue,\n2383:\t\t\t\t\t\t \u0026pfifo_qdisc_ops,\n2384:\t\t\t\t\t\t TC_H_MAKE(TC_H_MAJ(sch-\u003ehandle),\n2385:\t\t\t\t\t\t\t TC_H_MIN(i + 1)),\n2386:\t\t\t\t\t\t extack);\n2387:\t\t\tif (!qdisc)\n2388:\t\t\t\treturn -ENOMEM;\n2389:\t\n2390:\t\t\tif (i \u003c dev-\u003ereal_num_tx_queues)\n2391:\t\t\t\tqdisc_hash_add(qdisc, false);\n2392:\t\n2393:\t\t\tq-\u003eqdiscs[i] = qdisc;\n2394:\t\t}\n2395:\t\n2396:\t\tfor (tc = 0; tc \u003c TC_QOPT_MAX_QUEUE; tc++)\n2397:\t\t\tq-\u003efp[tc] = TC_FP_EXPRESS;\n2398:\t\n2399:\t\ttaprio_detect_broken_mqprio(q);\n2400:\t\n2401:\t\treturn taprio_change(sch, opt, extack);\n2402:\t}\n2403:\t\n2404:\tstatic void taprio_attach(struct Qdisc *sch)\n2405:\t{\n2406:\t\tstruct taprio_sched *q = qdisc_priv(sch);\n2407:\t\tstruct net_device *dev = qdisc_dev(sch);\n2408:\t\tunsigned int ntx;\n2409:\t\n"
} |
| 21/2 | 2026/10/16 14:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 22344 output: 30 thoughts: 670 Thoughts: **Worth Fuzzing - A Deep Dive into the Taprio Scheduler Modification** Okay, so let me break down my assessment of this patch. First, `taprio_init` is setting up taprio on a multi-queue netdevice, and then calling `taprio_change`. This is important because `taprio_change` itself is triggered whenever the userspace modifies the taprio schedule using netlink, specifically with `RTM_NEWQDISC`. That's a direct entry point. Furthermore, `advance_sched` runs when the `advance_timer` expires, and we know `find_entry_to_transmit`, `is_valid_interval`, and `get_packet_txtime` are running during packet enqueuing. The patch itself is touching multiple critical functions. We have `advance_sched`, `taprio_change`, `parse_taprio_schedule`, `find_entry_to_transmit`, `is_valid_interval`, `get_packet_txtime`. On top of that, it's introducing new functions like `taprio_advance_one_entry`, `taprio_fast_forward_sched`, and `taprio_set_entry_state`. Now, does this meet the reachability criteria? Absolutely. `sch_taprio.c` is software packet scheduling code within `net/sched/`. Any netdev created by userspace using netlink commands can run the taprio qdisc – that means tap, veth, dummy, loopback, or multiqueue netdevs, all of which are standard. This code is definitely reachable. Looking at the positive criteria, this patch hits all the right spots. We're dealing with core networking – QoS packet scheduling. The modifications are deep: it's altering scheduling logic, impacting hrtimer callback handling (`advance_sched`), introducing schedule fast-forwarding (`taprio_fast_forward_sched`), modifying list iteration and entry deletion, as well as schedule switching and error rollback within `taprio_change`. Furthermore, there are complex control flows involved, for example a `while (ktime_compare(end_time, now) <= 0)` loop with an `again:` label in `taprio_fast_forward_sched`, and list iteration/deletion in `parse_taprio_schedule`. The potential for edge cases, off-by-one errors, infinite loops, locking issues, and use-after-free bugs is high in these types of structures. These are definitely worth fuzzing. The question of required configs for `sch_taprio.c` must be determined by looking at the `Makefile` in `net/sched/`. |
| 22/2 | 2026/10/16 14:50 | tool | grepper |
0mArgs:{
"Expression": "sch_taprio",
"PathPrefixes": [
"net/sched/Makefile"
]
}Results: {
"Output": "net/sched/Makefile=63=obj-$(CONFIG_NET_SCH_ETF)\t+= sch_etf.o\nnet/sched/Makefile:64:obj-$(CONFIG_NET_SCH_TAPRIO)\t+= sch_taprio.o\nnet/sched/Makefile-65-obj-$(CONFIG_NET_SCH_BPF)\t+= bpf_qdisc.o\n"
} |
| 23/2 | 2026/10/16 14:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23711 output: 29 thoughts: 95 Thoughts: Here's my thought process, as it stands: **Digging into TAPRIO Configuration Dependencies** Okay, so I'm revisiting the Kconfig setup, specifically the `NET_SCH_TAPRIO` option. Let's trace its dependencies. First, I'll need to locate where `NET_SCH_TAPRIO` is defined within the Kconfig files. Alright, so it's in `net/sched/Kconfig`. Excellent. Now, what does `NET_SCH_TAPRIO` depend on? Let's see... it falls under the `menuconfig NET_SCHED` block. Right, right, so `NET_SCH_TAPRIO` is only visible if `NET_SCHED` is configured. This means I need to understand what controls `NET_SCHED`. Now, let me find `net/Kconfig` and then what depends on `NET_SCHED`. |
| 24/2 | 2026/10/16 14:50 | tool | grepper |
0mError:no matches Args: {
"Expression": "NET_SCHED",
"PathPrefixes": [
"net/Kconfig"
]
}Results: {
"error": "no matches"
} |
| 25/2 | 2026/10/16 14:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24041 output: 29 thoughts: 22 Thoughts: Here's a summary of my thought process: **Kconfig Dependency Check** Okay, time to figure out the dependencies here. My immediate thought is to check the `net/Kconfig` file. Specifically, I'm looking for a line that sources the `net/sched/Kconfig` file. This is the standard way to incorporate sub-system configuration into the main configuration structure, so it's a logical first step to trace the connection. I'll need to confirm if that source command exists and, if so, what its implications are. This should tell me how `net/sched` is integrated into the broader network configuration framework. |
| 26/2 | 2026/10/16 14:50 | tool | grepper |
0mArgs:{
"Expression": "net/sched/Kconfig",
"PathPrefixes": [
"net/"
]
}Results: {
"Output": "net/Kconfig=269=source \"net/mac802154/Kconfig\"\nnet/Kconfig:270:source \"net/sched/Kconfig\"\nnet/Kconfig-271-source \"net/dcb/Kconfig\"\n"
} |
| 27/2 | 2026/10/16 14:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24304 output: 32 thoughts: 17 Thoughts: Here's a summary of my thought process, keeping in mind my expert understanding: **Navigating the Configuration Landscape** Okay, so I'm thinking, based on the context... let's zero in on a specific area of the kernel configuration system. I have a hunch that the relevant setting, or perhaps the problem, might be in `net/Kconfig`. It's a key file for network-related options, and I have a pretty good mental map of where things are usually defined. I'm going to scan around line 270. That's a reasonable place to start, given my knowledge of the typical structure and how dependencies are often handled in that region of the file. From there, I can investigate, search for the key words and explore related settings. This should be a good starting point for my diagnosis or configuration. |
| 28/2 | 2026/10/16 14:50 | tool | read-file |
0mArgs:{
"File": "net/Kconfig",
"FirstLine": 250,
"LineCount": 30
}Results: {
"Contents": " 250:\tsource \"net/bridge/netfilter/Kconfig\"\n 251:\t\n 252:\tendif # if NETFILTER\n 253:\t\n 254:\tsource \"net/sctp/Kconfig\"\n 255:\tsource \"net/rds/Kconfig\"\n 256:\tsource \"net/tipc/Kconfig\"\n 257:\tsource \"net/atm/Kconfig\"\n 258:\tsource \"net/l2tp/Kconfig\"\n 259:\tsource \"net/802/Kconfig\"\n 260:\tsource \"net/bridge/Kconfig\"\n 261:\tsource \"net/dsa/Kconfig\"\n 262:\tsource \"net/8021q/Kconfig\"\n 263:\tsource \"net/llc/Kconfig\"\n 264:\tsource \"net/x25/Kconfig\"\n 265:\tsource \"net/lapb/Kconfig\"\n 266:\tsource \"net/phonet/Kconfig\"\n 267:\tsource \"net/6lowpan/Kconfig\"\n 268:\tsource \"net/ieee802154/Kconfig\"\n 269:\tsource \"net/mac802154/Kconfig\"\n 270:\tsource \"net/sched/Kconfig\"\n 271:\tsource \"net/dcb/Kconfig\"\n 272:\tsource \"net/dns_resolver/Kconfig\"\n 273:\tsource \"net/batman-adv/Kconfig\"\n 274:\tsource \"net/openvswitch/Kconfig\"\n 275:\tsource \"net/vmw_vsock/Kconfig\"\n 276:\tsource \"net/netlink/Kconfig\"\n 277:\tsource \"net/mpls/Kconfig\"\n 278:\tsource \"net/nsh/Kconfig\"\n 279:\tsource \"net/hsr/Kconfig\"\n"
} |
| 29/2 | 2026/10/16 14:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25052 output: 33 thoughts: 120 Thoughts: **Analyzing Network Scheduler Configurations** Okay, so I see that `net/Kconfig` is included, obviously, if the `NET` option is enabled. Right, makes sense. Now I'm looking at specific scheduler configurations. I see `CONFIG_NET_SCHED` and `CONFIG_NET_SCH_TAPRIO`. My immediate question is, what about `NET_SCH_MQPRIO_LIB`? I need to understand the dependencies here. Ah, here we go. It looks like `NET_SCH_TAPRIO` explicitly has a `select` statement for `NET_SCH_MQPRIO_LIB`. So, that means selecting `NET_SCH_TAPRIO` will *automatically* select `NET_SCH_MQPRIO_LIB`. Good, that's clean and efficient. Now, I think it's worth a quick check to see if `sch_taprio.c` itself uses any other configurations directly. Let's dig into the code and make sure there are no other dependencies I'm missing. It's always good to be thorough. |
| 30/2 | 2026/10/16 14:50 | tool | grepper |
0mError:no matches Args: {
"Expression": "CONFIG_",
"PathPrefixes": [
"net/sched/sch_taprio.c"
]
}Results: {
"error": "no matches"
} |
| 31/2 | 2026/10/16 14:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25450 output: 288 thoughts: 2125 Thoughts: **Focus Symbol and Configuration Analysis for `sch_taprio.c` Fuzzing** Alright, let's break down this `sch_taprio.c` analysis. My primary goal is to identify the crucial entry points and configurations to effectively guide a fuzzer. First things first, there are no conditional compilation directives using `#ifdef CONFIG_` within the file, which tells me the key configurations are `NET_SCH_TAPRIO` and its parent `NET_SCHED`. Now, the core of this assessment lies in pinpointing the functions that represent the "feature logic" within the modified code. I need to avoid getting bogged down in ubiquitous functions. Based on the diff, here's the initial list of functions that have been introduced or significantly changed: `taprio_min_interval`, `taprio_update_queue_max_sdu`, `find_entry_to_transmit`, `is_valid_interval`, `get_packet_txtime`, `should_change_schedules`, `taprio_set_entry_state`, `taprio_advance_one_entry`, `taprio_fast_forward_sched`, `advance_sched`, `parse_taprio_schedule`, `setup_first_end_time`, `taprio_dev_notifier`, `taprio_enable_offload`, `taprio_parse_tc_entries`, and `taprio_change`. I need to determine which of these are likely to have standalone symbols that the fuzzer can target. Given that most of these are `static`, inlining is a potential issue. However, `advance_sched` is used as a function pointer in `hrtimer_setup`, and that *forces* a standalone symbol. Similarly, `taprio_change` is accessed via `taprio_qdisc_ops.change` and `taprio_dev_notifier` in a notifier call, so those have distinct symbols. `taprio_fast_forward_sched`, `taprio_advance_one_entry`, and `parse_taprio_schedule`, however, are more complex. They *could* be inlined. The critical factor is whether these are non-trivial functions called within the loops of `advance_sched`, which would mean they have standalone symbol addresses. Also, `find_entry_to_transmit` is called by `is_valid_interval` and `get_packet_txtime`, so I'll check its symbol status. The guideline stresses avoiding the fuzzing of "ubiquitous lifecycle hot-paths" such as memory allocators and page fault/trap handlers. Since all of these functions are within `sch_taprio.c`, which implements the logic for Time-Aware Shaper (TAPRIO) qdisc functionality, I conclude none of them are ubiquitous. I'm focusing on the heart of the changes here. Diving deeper, let's look at the modified functions, and select the specific functions to target: 1. `advance_sched`: The hrtimer callback, this function is the schedule progression logic, rewritten and essential to the scheduling mechanism. 2. `taprio_fast_forward_sched`: New function implementing schedule fast forwarding. This suggests a new control path with the introduction of fast forwarding. 3. `taprio_advance_one_entry`: New function, essential to advancing one entry in the schedule. 4. `taprio_change`: The qdisc configuration function which parses attributes, sets up the schedule, and manages rollback on errors. 5. `parse_taprio_schedule`: Major changes to schedule interval parsing, cycle\_time validation, entry truncation and deletion. 6. `find_entry_to_transmit`: Modified packet transmission interval matching logic. 7. `get_packet_txtime`: Modified packet TX time calculation and schedule switching logic. 8. `is_valid_interval`: Modified interval validation for packets with `SO_TXTIME`. I'll double-check to be certain none of these are ubiquitous. I've re-reviewed and I am confident that they are not. Next, I'll go through and check if any other functions were modified in `sch_taprio.c`. Based on my review of the code and the `git diff`, I have identified the functions that also deserve examination: `taprio_update_queue_max_sdu` `taprio_min_interval` `taprio_set_entry_state` `taprio_enable_offload` `taprio_parse_tc_entries` `should_change_schedules` `setup_first_end_time` `taprio_dev_notifier` However, the most relevant focus symbols are: `advance_sched`, `taprio_change`, ` |
| 32/2 | 2026/10/16 14:51 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"NET_SCHED",
"NET_SCH_TAPRIO"
],
"FocusSymbols": [
"advance_sched",
"taprio_fast_forward_sched",
"taprio_advance_one_entry",
"taprio_change",
"parse_taprio_schedule",
"find_entry_to_transmit",
"get_packet_txtime",
"is_valid_interval"
],
"Reasoning": "The patch modifies the TAPRIO (Time-Aware Priority) traffic control queueing discipline (sch_taprio.c) in the networking subsystem. TAPRIO is fully reachable in standard virtualized environments (e.g. using virtual network devices like veth or dummy via rtnetlink). The patch introduces significant logic changes: schedule advancement and fast-forwarding loops in the hrtimer callback (advance_sched, taprio_fast_forward_sched, taprio_advance_one_entry), schedule validation and entry truncation/deletion in parse_taprio_schedule, interval calculations in find_entry_to_transmit and get_packet_txtime, and error-path rollback in taprio_change. These complex timing, looping, and list-handling changes warrant fuzzing to detect regressions, infinite loops, and race conditions.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"NET_SCHED",
"NET_SCH_TAPRIO"
],
"FocusSymbols": [
"advance_sched",
"taprio_fast_forward_sched",
"taprio_advance_one_entry",
"taprio_change",
"parse_taprio_schedule",
"find_entry_to_transmit",
"get_packet_txtime",
"is_valid_interval"
],
"Reasoning": "The patch modifies the TAPRIO (Time-Aware Priority) traffic control queueing discipline (sch_taprio.c) in the networking subsystem. TAPRIO is fully reachable in standard virtualized environments (e.g. using virtual network devices like veth or dummy via rtnetlink). The patch introduces significant logic changes: schedule advancement and fast-forwarding loops in the hrtimer callback (advance_sched, taprio_fast_forward_sched, taprio_advance_one_entry), schedule validation and entry truncation/deletion in parse_taprio_schedule, interval calculations in find_entry_to_transmit and get_packet_txtime, and error-path rollback in taprio_change. These complex timing, looping, and list-handling changes warrant fuzzing to detect regressions, infinite loops, and race conditions.",
"WorthFuzzing": true
} |
| 33/1 | 2026/10/16 14:51 | action | If |
3mArgs:{
"WorthFuzzing": true
}Results: null |
| 34/2 | 2026/10/16 14:51 | agent | kmsan-evaluator |
3mModel:core Args: null Results: {
"KMSANReasoning": "The patch modifies net/sched/sch_taprio.c to improve schedule validation, prevent timer storms by enforcing a minimum interval and fast-forwarding laggy schedules, and cleanly roll back device/qdisc configurations on failure during taprio_change().\n\nKMSAN vs KASAN Applicability:\n- Uninitialized memory / KMSAN: All newly introduced stack arrays (`max_sdu`, `fp`) are explicitly zero-initialized (`= { }`). The rollback buffers (`old_tc_to_txq`, `old_prio_tc_map`, `old_cur_txq`) are guarded by the `tc_configured` flag and initialized via `memcpy` before use. Schedule structures are allocated using `kzalloc_obj`. No structures are copied to userspace and no uninitialized fields are read in branching or control flow. Therefore, there are no uninitialized memory usage or kernel-to-user info-leak risks.\n- KASAN / LOCKDEP / Standard detectors: The risks in this patch revolve around list handling of schedule entries, timer expiration math, locking with `q-\u003ecurrent_entry_lock`, and RCU schedule switching. Bugs in these areas (such as use-after-free, double free, deadlocks, or out-of-bounds array access) are effectively detected by KASAN and LOCKDEP.\n\nConsequently, a dedicated KMSAN fuzzing session is not justified.",
"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: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit b51f9aac51b713ad5bb922900aeb8687e60cc7e4
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Oct 16 14:48:53 2026 +0000
syz-cluster: applied patch under review
diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 39ac5b97aa3af..9128f8179a993 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -48,6 +48,10 @@ static struct static_key_false taprio_have_working_mqprio;
* 60 * 17 > PSEC_PER_NSEC (1000)
*/
#define TAPRIO_PICOS_PER_BYTE_MIN 17
+/* Minimum interval for software mode (hrtimer-driven advance_sched) to
+ * avoid hrtimer interrupt storms and CPU starvation.
+ */
+#define TAPRIO_MIN_SW_INTERVAL_NS (10 * NSEC_PER_USEC)
struct sched_entry {
/* Durations between this GCL entry and the GCL entry where the
@@ -259,6 +263,17 @@ static int length_to_duration(struct taprio_sched *q, int len)
return div_u64(len * atomic64_read(&q->picos_per_byte), PSEC_PER_NSEC);
}
+static int taprio_min_interval(struct taprio_sched *q)
+{
+ int min_duration = length_to_duration(q, ETH_ZLEN);
+
+ if (!FULL_OFFLOAD_IS_ENABLED(q->flags) &&
+ !TXTIME_ASSIST_IS_ENABLED(q->flags))
+ min_duration = max_t(int, min_duration, TAPRIO_MIN_SW_INTERVAL_NS);
+
+ return min_duration;
+}
+
static int duration_to_length(struct taprio_sched *q, u64 duration)
{
return div_u64(duration * PSEC_PER_NSEC, atomic64_read(&q->picos_per_byte));
@@ -270,7 +285,8 @@ static int duration_to_length(struct taprio_sched *q, u64 duration)
*/
static void taprio_update_queue_max_sdu(struct taprio_sched *q,
struct sched_gate_list *sched,
- struct qdisc_size_table *stab)
+ struct qdisc_size_table *stab,
+ const u32 max_sdu_user[TC_MAX_QUEUE])
{
struct net_device *dev = qdisc_dev(q->root);
int num_tc = netdev_get_num_tc(dev);
@@ -280,7 +296,7 @@ static void taprio_update_queue_max_sdu(struct taprio_sched *q,
int tc;
for (tc = 0; tc < num_tc; tc++) {
- max_sdu_from_user = q->max_sdu[tc] ?: U32_MAX;
+ max_sdu_from_user = max_sdu_user[tc] ?: U32_MAX;
/* TC gate never closes => keep the queueMaxSDU
* selected by the user
@@ -357,7 +373,7 @@ static struct sched_entry *find_entry_to_transmit(struct sk_buff *skb,
curr_intv_end = get_interval_end_time(sched, admin, entry,
curr_intv_start);
- if (ktime_after(curr_intv_start, cycle_end))
+ if (ktime_compare(curr_intv_start, cycle_end) >= 0)
break;
if (!(entry->gate_mask & BIT(tc)) ||
@@ -366,10 +382,10 @@ static struct sched_entry *find_entry_to_transmit(struct sk_buff *skb,
txtime = entry->next_txtime;
- if (ktime_before(txtime, time) || validate_interval) {
+ if (!ktime_after(txtime, time) || validate_interval) {
transmit_end_time = ktime_add_ns(time, packet_transmit_time);
- if ((ktime_before(curr_intv_start, time) &&
- ktime_before(transmit_end_time, curr_intv_end)) ||
+ if ((!ktime_after(curr_intv_start, time) &&
+ !ktime_after(transmit_end_time, curr_intv_end)) ||
(ktime_after(curr_intv_start, time) && !validate_interval)) {
entry_found = entry;
*interval_start = curr_intv_start;
@@ -405,8 +421,19 @@ static bool is_valid_interval(struct sk_buff *skb, struct Qdisc *sch)
struct sched_entry *entry;
rcu_read_lock();
- sched = rcu_dereference(q->oper_sched);
admin = rcu_dereference(q->admin_sched);
+ sched = rcu_dereference(q->oper_sched);
+
+ if (admin && !ktime_before(skb->tstamp, admin->base_time)) {
+ sched = admin;
+ admin = NULL;
+ }
+
+ /* Until the schedule starts, all the queues are open */
+ if (!sched || ktime_before(skb->tstamp, sched->base_time)) {
+ rcu_read_unlock();
+ return true;
+ }
entry = find_entry_to_transmit(skb, sch, sched, admin, skb->tstamp,
&interval_start, &interval_end, true);
@@ -484,7 +511,7 @@ static long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)
rcu_read_lock();
admin = rcu_dereference(q->admin_sched);
sched = rcu_dereference(q->oper_sched);
- if (admin && ktime_after(minimum_time, admin->base_time))
+ if (admin && !ktime_before(minimum_time, admin->base_time))
switch_schedules(q, &admin, &sched);
/* Until the schedule starts, all the queues are open */
@@ -513,8 +540,9 @@ static long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)
txtime = max_t(ktime_t, txtime, interval_start);
if (admin && admin != sched &&
- ktime_after(txtime, admin->base_time)) {
+ !ktime_before(txtime, admin->base_time)) {
sched = admin;
+ admin = NULL;
sched_changed = true;
continue;
}
@@ -888,7 +916,7 @@ static bool should_change_schedules(const struct sched_gate_list *admin,
{
ktime_t next_base_time, extension_time;
- if (!admin)
+ if (!admin || !oper)
return false;
next_base_time = sched_base_time(admin);
@@ -916,6 +944,142 @@ static bool should_change_schedules(const struct sched_gate_list *admin,
return false;
}
+static void taprio_set_entry_state(struct taprio_sched *q,
+ struct sched_gate_list *oper,
+ struct sched_entry *next,
+ ktime_t cur_start,
+ ktime_t end_time,
+ int num_tc)
+{
+ int tc;
+
+ for (tc = 0; tc < num_tc; tc++) {
+ if (next->gate_duration[tc] == oper->cycle_time)
+ next->gate_close_time[tc] = KTIME_MAX;
+ else
+ next->gate_close_time[tc] = ktime_add_ns(cur_start,
+ next->gate_duration[tc]);
+ }
+
+ next->end_time = end_time;
+ taprio_set_budgets(q, oper, next);
+}
+
+static void taprio_advance_one_entry(struct taprio_sched *q,
+ struct sched_gate_list **p_oper,
+ struct sched_gate_list **p_admin,
+ struct sched_entry **p_entry,
+ struct sched_entry **p_next,
+ ktime_t *p_end_time,
+ int num_tc)
+{
+ struct sched_gate_list *oper = *p_oper;
+ struct sched_gate_list *admin = *p_admin;
+ struct sched_entry *entry = *p_entry;
+ ktime_t cur_start, end_time;
+ struct sched_entry *next;
+
+ if (!oper->cycle_end_time)
+ oper->cycle_end_time = ktime_add_ns(sched_base_time(oper),
+ oper->cycle_time);
+
+ if (should_restart_cycle(oper, entry)) {
+ next = list_first_entry(&oper->entries, struct sched_entry,
+ list);
+ oper->cycle_end_time = ktime_add_ns(oper->cycle_end_time,
+ oper->cycle_time);
+ } else {
+ next = list_next_entry(entry, list);
+ }
+
+ cur_start = entry->end_time;
+ end_time = ktime_add_ns(cur_start, next->interval);
+ end_time = min_t(ktime_t, end_time, oper->cycle_end_time);
+
+ if (should_change_schedules(admin, oper, end_time)) {
+ switch_schedules(q, &admin, &oper);
+ if (!oper->cycle_end_time)
+ oper->cycle_end_time = ktime_add_ns(sched_base_time(oper),
+ oper->cycle_time);
+ cur_start = ktime_sub_ns(oper->cycle_end_time,
+ oper->cycle_time);
+ next = list_first_entry(&oper->entries, struct sched_entry, list);
+ end_time = ktime_add_ns(cur_start, next->interval);
+ end_time = min_t(ktime_t, end_time, oper->cycle_end_time);
+ *p_admin = admin;
+ *p_oper = oper;
+ }
+
+ taprio_set_entry_state(q, oper, next, cur_start, end_time, num_tc);
+
+ *p_entry = next;
+ *p_next = next;
+ *p_end_time = end_time;
+}
+
+static void taprio_fast_forward_sched(struct taprio_sched *q,
+ struct sched_gate_list **p_oper,
+ struct sched_gate_list **p_admin,
+ struct sched_entry **p_next,
+ ktime_t *p_end_time,
+ ktime_t now,
+ int num_tc)
+{
+ struct sched_gate_list *oper = *p_oper;
+ struct sched_gate_list *admin = *p_admin;
+ struct sched_entry *entry, *next;
+ ktime_t end_time, cur_start;
+ s64 diff;
+
+ if (admin && ktime_compare(sched_base_time(admin), now) <= 0)
+ switch_schedules(q, &admin, &oper);
+
+again:
+ if (!oper->cycle_end_time)
+ oper->cycle_end_time = ktime_add_ns(sched_base_time(oper),
+ oper->cycle_time);
+
+ diff = ktime_sub(now, oper->cycle_end_time);
+ if (diff >= 0) {
+ u64 cycles = div64_u64(diff, oper->cycle_time) + 1;
+
+ oper->cycle_end_time = ktime_add_ns(oper->cycle_end_time,
+ cycles * oper->cycle_time);
+ }
+
+ cur_start = ktime_sub_ns(oper->cycle_end_time, oper->cycle_time);
+ next = list_last_entry(&oper->entries, struct sched_entry, list);
+ end_time = oper->cycle_end_time;
+
+ list_for_each_entry(entry, &oper->entries, list) {
+ ktime_t cur_end = ktime_add_ns(cur_start, entry->interval);
+
+ if (list_is_last(&entry->list, &oper->entries))
+ cur_end = oper->cycle_end_time;
+ else
+ cur_end = min_t(ktime_t, cur_end, oper->cycle_end_time);
+
+ if (ktime_compare(cur_end, now) > 0) {
+ next = entry;
+ end_time = cur_end;
+ break;
+ }
+ cur_start = cur_end;
+ }
+
+ if (should_change_schedules(admin, oper, end_time)) {
+ switch_schedules(q, &admin, &oper);
+ goto again;
+ }
+
+ taprio_set_entry_state(q, oper, next, cur_start, end_time, num_tc);
+
+ *p_oper = oper;
+ *p_admin = admin;
+ *p_next = next;
+ *p_end_time = end_time;
+}
+
static enum hrtimer_restart advance_sched(struct hrtimer *timer)
{
struct taprio_sched *q = container_of(timer, struct taprio_sched,
@@ -925,8 +1089,7 @@ static enum hrtimer_restart advance_sched(struct hrtimer *timer)
int num_tc = netdev_get_num_tc(dev);
struct sched_entry *entry, *next;
struct Qdisc *sch = q->root;
- ktime_t end_time;
- int tc;
+ ktime_t end_time, now;
spin_lock(&q->current_entry_lock);
entry = rcu_dereference_protected(q->current_entry,
@@ -939,6 +1102,11 @@ static enum hrtimer_restart advance_sched(struct hrtimer *timer)
if (!oper)
switch_schedules(q, &admin, &oper);
+ if (unlikely(!oper)) {
+ spin_unlock(&q->current_entry_lock);
+ return HRTIMER_NORESTART;
+ }
+
/* This can happen in two cases: 1. this is the very first run
* of this function (i.e. we weren't running any schedule
* previously); 2. The previous schedule just ended. The first
@@ -946,49 +1114,61 @@ static enum hrtimer_restart advance_sched(struct hrtimer *timer)
* schedule initialization.
*/
if (unlikely(!entry || entry->end_time == oper->base_time)) {
- next = list_first_entry(&oper->entries, struct sched_entry,
- list);
- end_time = next->end_time;
- goto first_run;
- }
+ ktime_t cur_start;
- if (should_restart_cycle(oper, entry)) {
+ if (!oper->cycle_end_time)
+ oper->cycle_end_time = ktime_add_ns(sched_base_time(oper),
+ oper->cycle_time);
+ cur_start = ktime_sub_ns(oper->cycle_end_time,
+ oper->cycle_time);
next = list_first_entry(&oper->entries, struct sched_entry,
list);
- oper->cycle_end_time = ktime_add_ns(oper->cycle_end_time,
- oper->cycle_time);
+ end_time = ktime_add_ns(cur_start, next->interval);
+ end_time = min_t(ktime_t, end_time, oper->cycle_end_time);
+
+ if (should_change_schedules(admin, oper, end_time)) {
+ switch_schedules(q, &admin, &oper);
+ if (!oper->cycle_end_time)
+ oper->cycle_end_time = ktime_add_ns(sched_base_time(oper),
+ oper->cycle_time);
+ cur_start = ktime_sub_ns(oper->cycle_end_time,
+ oper->cycle_time);
+ next = list_first_entry(&oper->entries,
+ struct sched_entry, list);
+ end_time = ktime_add_ns(cur_start, next->interval);
+ end_time = min_t(ktime_t, end_time,
+ oper->cycle_end_time);
+ }
+
+ taprio_set_entry_state(q, oper, next, cur_start, end_time,
+ num_tc);
} else {
- next = list_next_entry(entry, list);
+ taprio_advance_one_entry(q, &oper, &admin, &entry, &next,
+ &end_time, num_tc);
}
- end_time = ktime_add_ns(entry->end_time, next->interval);
- end_time = min_t(ktime_t, end_time, oper->cycle_end_time);
-
- for (tc = 0; tc < num_tc; tc++) {
- if (next->gate_duration[tc] == oper->cycle_time)
- next->gate_close_time[tc] = KTIME_MAX;
- else
- next->gate_close_time[tc] = ktime_add_ns(entry->end_time,
- next->gate_duration[tc]);
- }
+ now = taprio_get_time(q);
+ if (unlikely(ktime_compare(end_time, now) <= 0)) {
+ size_t max_steps = min_t(size_t, oper->num_entries, 32);
+ size_t i;
+
+ for (i = 0; i < max_steps && ktime_compare(end_time, now) <= 0; i++) {
+ entry = next;
+ taprio_advance_one_entry(q, &oper, &admin, &entry, &next,
+ &end_time, num_tc);
+ }
- if (should_change_schedules(admin, oper, end_time)) {
- switch_schedules(q, &admin, &oper);
- /* After changing schedules, the next entry is the first one
- * in the new schedule, with a pre-calculated end_time.
- */
- next = list_first_entry(&oper->entries, struct sched_entry, list);
- end_time = next->end_time;
+ now = taprio_get_time(q);
+ while (ktime_compare(end_time, now) <= 0) {
+ taprio_fast_forward_sched(q, &oper, &admin, &next,
+ &end_time, now, num_tc);
+ now = taprio_get_time(q);
+ }
}
- next->end_time = end_time;
- taprio_set_budgets(q, oper, next);
-
-first_run:
rcu_assign_pointer(q->current_entry, next);
- spin_unlock(&q->current_entry_lock);
-
hrtimer_set_expires(&q->advance_timer, end_time);
+ spin_unlock(&q->current_entry_lock);
rcu_read_lock();
__netif_schedule(sch);
@@ -1130,7 +1310,11 @@ static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,
struct sched_gate_list *new,
struct netlink_ext_ack *extack)
{
+ int min_duration = taprio_min_interval(q);
+ struct sched_entry *entry, *n;
+ s64 elapsed = 0;
int err = 0;
+ int i = 0;
if (tb[TCA_TAPRIO_ATTR_SCHED_SINGLE_ENTRY]) {
NL_SET_ERR_MSG(extack, "Adding a single entry is not supported");
@@ -1152,8 +1336,12 @@ static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,
if (err < 0)
return err;
+ if (list_empty(&new->entries)) {
+ NL_SET_ERR_MSG(extack, "There should be at least one entry in the schedule");
+ return -EINVAL;
+ }
+
if (!new->cycle_time) {
- struct sched_entry *entry;
ktime_t cycle = 0;
list_for_each_entry(entry, &new->entries, list)
@@ -1167,12 +1355,45 @@ static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,
new->cycle_time = cycle;
}
- if (new->cycle_time < new->num_entries * length_to_duration(q, ETH_ZLEN)) {
+ if (new->cycle_time < min_duration) {
NL_SET_ERR_MSG(extack, "'cycle_time' is too small");
return -EINVAL;
}
- taprio_calculate_gate_durations(q, new);
+ list_for_each_entry_safe(entry, n, &new->entries, list) {
+ if (elapsed >= new->cycle_time) {
+ list_del(&entry->list);
+ kfree(entry);
+ new->num_entries--;
+ continue;
+ }
+
+ if (elapsed + entry->interval > new->cycle_time) {
+ entry->interval = new->cycle_time - elapsed;
+ elapsed = new->cycle_time;
+ } else {
+ elapsed += entry->interval;
+ }
+ }
+
+ if (list_empty(&new->entries)) {
+ NL_SET_ERR_MSG(extack, "There should be at least one entry in the schedule");
+ return -EINVAL;
+ }
+
+ if (elapsed < new->cycle_time) {
+ entry = list_last_entry(&new->entries, struct sched_entry, list);
+ entry->interval += new->cycle_time - elapsed;
+ }
+
+ list_for_each_entry(entry, &new->entries, list) {
+ if (entry->interval < min_duration) {
+ NL_SET_ERR_MSG(extack, "Invalid interval for schedule entry");
+ return -EINVAL;
+ }
+ entry->index = i++;
+ }
+ new->num_entries = i;
return 0;
}
@@ -1256,7 +1477,7 @@ static void setup_first_end_time(struct taprio_sched *q,
/* FIXME: find a better place to do this */
sched->cycle_end_time = ktime_add_ns(base, cycle);
- first->end_time = ktime_add_ns(base, first->interval);
+ first->end_time = ktime_add_ns(base, min_t(s64, first->interval, cycle));
taprio_set_budgets(q, sched, first);
for (tc = 0; tc < num_tc; tc++) {
@@ -1349,11 +1570,11 @@ static int taprio_dev_notifier(struct notifier_block *nb, unsigned long event,
rcu_read_lock();
oper = rcu_dereference(q->oper_sched);
if (oper)
- taprio_update_queue_max_sdu(q, oper, stab);
+ taprio_update_queue_max_sdu(q, oper, stab, q->max_sdu);
admin = rcu_dereference(q->admin_sched);
if (admin)
- taprio_update_queue_max_sdu(q, admin, stab);
+ taprio_update_queue_max_sdu(q, admin, stab, q->max_sdu);
rcu_read_unlock();
break;
@@ -1516,6 +1737,8 @@ static void taprio_cleanup_broken_mqprio(struct taprio_sched *q)
static int taprio_enable_offload(struct net_device *dev,
struct taprio_sched *q,
struct sched_gate_list *sched,
+ const u32 max_sdu[TC_MAX_QUEUE],
+ u32 fp[TC_QOPT_MAX_QUEUE],
struct netlink_ext_ack *extack)
{
const struct net_device_ops *ops = dev->netdev_ops;
@@ -1534,7 +1757,7 @@ static int taprio_enable_offload(struct net_device *dev,
if (!caps.supports_queue_max_sdu) {
for (tc = 0; tc < TC_MAX_QUEUE; tc++) {
- if (q->max_sdu[tc]) {
+ if (max_sdu[tc]) {
NL_SET_ERR_MSG_MOD(extack,
"Device does not handle queueMaxSDU");
return -EOPNOTSUPP;
@@ -1553,10 +1776,10 @@ static int taprio_enable_offload(struct net_device *dev,
mqprio_qopt_reconstruct(dev, &offload->mqprio.qopt);
offload->mqprio.extack = extack;
taprio_sched_to_offload(dev, sched, offload, &caps);
- mqprio_fp_to_offload(q->fp, &offload->mqprio);
+ mqprio_fp_to_offload(fp, &offload->mqprio);
for (tc = 0; tc < TC_MAX_QUEUE; tc++)
- offload->max_sdu[tc] = q->max_sdu[tc];
+ offload->max_sdu[tc] = max_sdu[tc];
err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TAPRIO, offload);
if (err < 0) {
@@ -1747,14 +1970,14 @@ static int taprio_parse_tc_entry(struct Qdisc *sch,
static int taprio_parse_tc_entries(struct Qdisc *sch,
struct nlattr *opt,
+ u32 max_sdu[TC_MAX_QUEUE],
+ u32 fp[TC_QOPT_MAX_QUEUE],
struct netlink_ext_ack *extack)
{
struct taprio_sched *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);
- u32 max_sdu[TC_QOPT_MAX_QUEUE];
bool have_preemption = false;
unsigned long seen_tcs = 0;
- u32 fp[TC_QOPT_MAX_QUEUE];
struct nlattr *n;
int tc, rem;
int err = 0;
@@ -1772,8 +1995,6 @@ static int taprio_parse_tc_entries(struct Qdisc *sch,
}
for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) {
- WRITE_ONCE(q->max_sdu[tc], max_sdu[tc]);
- WRITE_ONCE(q->fp[tc], fp[tc]);
if (fp[tc] != TC_FP_EXPRESS)
have_preemption = true;
}
@@ -1792,7 +2013,7 @@ static int taprio_parse_tc_entries(struct Qdisc *sch,
}
}
- return err;
+ return 0;
}
static int taprio_mqprio_cmp(const struct net_device *dev,
@@ -1823,15 +2044,33 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
{
struct qdisc_size_table *stab = rtnl_dereference(sch->stab);
+ struct sched_gate_list *oper, *admin, *new_admin = NULL;
+ struct netdev_tc_txq old_tc_to_txq[TC_MAX_QUEUE];
struct nlattr *tb[TCA_TAPRIO_ATTR_MAX + 1] = { };
- struct sched_gate_list *oper, *admin, *new_admin;
+ u8 old_prio_tc_map[TC_BITMASK + 1];
+ int old_cur_txq[TC_MAX_QUEUE];
+ enum tk_offsets old_tk_offset;
struct taprio_sched *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);
struct tc_mqprio_qopt *mqprio = NULL;
+ u32 max_sdu[TC_MAX_QUEUE] = { };
+ u32 fp[TC_QOPT_MAX_QUEUE] = { };
+ bool tc_configured = false;
+ int old_clockid, i, err;
unsigned long flags;
+ bool initial_setup;
u32 taprio_flags;
+ s16 old_num_tc;
+ u32 old_flags;
ktime_t start;
- int i, err;
+
+ oper = rtnl_dereference(q->oper_sched);
+ admin = rtnl_dereference(q->admin_sched);
+ initial_setup = (!oper && !admin);
+
+ old_flags = q->flags;
+ old_clockid = q->clockid;
+ old_tk_offset = q->tk_offset;
err = nla_parse_nested_deprecated(tb, TCA_TAPRIO_ATTR_MAX, opt,
taprio_policy, extack);
@@ -1866,41 +2105,68 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
WRITE_ONCE(q->flags, taprio_flags);
}
+ if (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]) {
+ if (!TXTIME_ASSIST_IS_ENABLED(q->flags)) {
+ NL_SET_ERR_MSG_MOD(extack, "txtime-delay can only be set when txtime-assist mode is enabled");
+ err = -EINVAL;
+ goto free_sched;
+ }
+ }
+
/* Needed for length_to_duration() during netlink attribute parsing */
taprio_set_picos_per_byte(dev, q, extack);
err = taprio_parse_mqprio_opt(dev, mqprio, extack, q->flags);
if (err < 0)
- return err;
+ goto free_sched;
- err = taprio_parse_tc_entries(sch, opt, extack);
+ err = taprio_parse_tc_entries(sch, opt, max_sdu, fp, extack);
if (err)
- return err;
+ goto free_sched;
new_admin = kzalloc_obj(*new_admin);
if (!new_admin) {
NL_SET_ERR_MSG(extack, "Not enough memory for a new schedule");
- return -ENOMEM;
+ err = -ENOMEM;
+ goto free_sched;
}
INIT_LIST_HEAD(&new_admin->entries);
- oper = rtnl_dereference(q->oper_sched);
- admin = rtnl_dereference(q->admin_sched);
-
/* no changes - no new mqprio settings */
if (!taprio_mqprio_cmp(dev, mqprio))
mqprio = NULL;
- if (mqprio && (oper || admin)) {
+ if (mqprio && !initial_setup) {
NL_SET_ERR_MSG(extack, "Changing the traffic mapping of a running schedule is not supported");
err = -ENOTSUPP;
goto free_sched;
}
+ err = parse_taprio_schedule(q, tb, new_admin, extack);
+ if (err < 0)
+ goto free_sched;
+
+ if (new_admin->num_entries == 0) {
+ NL_SET_ERR_MSG(extack, "There should be at least one entry in the schedule");
+ err = -EINVAL;
+ goto free_sched;
+ }
+
+ err = taprio_parse_clockid(sch, tb, extack);
+ if (err < 0)
+ goto free_sched;
+
if (mqprio) {
+ old_num_tc = dev->num_tc;
+ memcpy(old_tc_to_txq, dev->tc_to_txq, sizeof(old_tc_to_txq));
+ memcpy(old_prio_tc_map, dev->prio_tc_map,
+ sizeof(old_prio_tc_map));
+ memcpy(old_cur_txq, q->cur_txq, sizeof(old_cur_txq));
+
err = netdev_set_num_tc(dev, mqprio->num_tc);
if (err)
goto free_sched;
+ tc_configured = true;
for (i = 0; i < mqprio->num_tc; i++) {
netdev_set_tc_queue(dev, i,
mqprio->count[i],
@@ -1914,24 +2180,12 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
mqprio->prio_tc_map[i]);
}
- err = parse_taprio_schedule(q, tb, new_admin, extack);
- if (err < 0)
- goto free_sched;
+ taprio_calculate_gate_durations(q, new_admin);
- if (new_admin->num_entries == 0) {
- NL_SET_ERR_MSG(extack, "There should be at least one entry in the schedule");
- err = -EINVAL;
- goto free_sched;
- }
-
- err = taprio_parse_clockid(sch, tb, extack);
- if (err < 0)
- goto free_sched;
-
- taprio_update_queue_max_sdu(q, new_admin, stab);
+ taprio_update_queue_max_sdu(q, new_admin, stab, max_sdu);
if (FULL_OFFLOAD_IS_ENABLED(q->flags))
- err = taprio_enable_offload(dev, q, new_admin, extack);
+ err = taprio_enable_offload(dev, q, new_admin, max_sdu, fp, extack);
else
err = taprio_disable_offload(dev, q, extack);
if (err)
@@ -1940,15 +2194,10 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
/* Protects against enqueue()/dequeue() */
spin_lock_bh(qdisc_lock(sch));
- if (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]) {
- if (!TXTIME_ASSIST_IS_ENABLED(q->flags)) {
- NL_SET_ERR_MSG_MOD(extack, "txtime-delay can only be set when txtime-assist mode is enabled");
- err = -EINVAL;
- goto unlock;
- }
-
- WRITE_ONCE(q->txtime_delay,
- nla_get_u32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]));
+ err = taprio_get_start_time(sch, new_admin, &start);
+ if (err < 0) {
+ NL_SET_ERR_MSG(extack, "Internal error: failed get start time");
+ goto unlock;
}
if (!TXTIME_ASSIST_IS_ENABLED(q->flags) &&
@@ -1957,11 +2206,14 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
hrtimer_setup(&q->advance_timer, advance_sched, q->clockid, HRTIMER_MODE_ABS);
}
- err = taprio_get_start_time(sch, new_admin, &start);
- if (err < 0) {
- NL_SET_ERR_MSG(extack, "Internal error: failed get start time");
- goto unlock;
- }
+ if (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY])
+ WRITE_ONCE(q->txtime_delay,
+ nla_get_u32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]));
+
+ for (i = 0; i < TC_MAX_QUEUE; i++)
+ WRITE_ONCE(q->max_sdu[i], max_sdu[i]);
+ for (i = 0; i < TC_QOPT_MAX_QUEUE; i++)
+ WRITE_ONCE(q->fp[i], fp[i]);
setup_txtime(q, new_admin, start);
@@ -2008,6 +2260,22 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
spin_unlock_bh(qdisc_lock(sch));
free_sched:
+ if (err && initial_setup) {
+ if (tc_configured) {
+ WRITE_ONCE(dev->num_tc, old_num_tc);
+ for (i = 0; i < TC_MAX_QUEUE; i++)
+ WRITE_ONCE(dev->tc_to_txq[i].combined,
+ old_tc_to_txq[i].combined);
+ for (i = 0; i <= TC_BITMASK; i++)
+ WRITE_ONCE(dev->prio_tc_map[i],
+ old_prio_tc_map[i]);
+ memcpy(q->cur_txq, old_cur_txq, sizeof(q->cur_txq));
+ }
+ WRITE_ONCE(q->flags, old_flags);
+ q->clockid = old_clockid;
+ WRITE_ONCE(q->tk_offset, old_tk_offset);
+ }
+
if (new_admin)
call_rcu(&new_admin->rcu, taprio_free_sched_cb);
|
| 35/3 | 2026/10/16 14:51 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 12334 output: 40 thoughts: 903 Thoughts: **KMSAN Fuzzing Session Assessment for `net/sched/sch_taprio.c` Patch**
Okay, so the question is whether this patch to `net/sched/sch_taprio.c` warrants a dedicated KMSAN fuzzing session. I need to be methodical here. First, let's remember the KMSAN trigger: it's all about uninitialized memory reads – stack, heap, and page allocations – and kernel-to-user memory leaks. The crucial rule is to only consider KMSAN if the potential bug isn't caught by KASAN, LOCKDEP, or other standard detectors. That's the baseline.
Now, let's dissect this patch piece by piece. The changes are:
1. **`TAPRIO_MIN_SW_INTERVAL_NS`**: A simple `#define` and usage. This is unlikely to introduce uninitialized memory issues on its own. It uses existing values or constants.
2. **`taprio_update_queue_max_sdu`**: This looks like the most interesting part. The patch is moving away from direct reads of `q->max_sdu` to passing in a `const u32 max_sdu_user[TC_MAX_QUEUE]` argument. It's getting these values from `taprio_change`. I need to ensure the values are initialized. The code shows `u32 max_sdu[TC_MAX_QUEUE] = { };` (zero-initialized) and `u32 fp[TC_QOPT_MAX_QUEUE] = { };` also zero-initialized. That's good.
3. - 7. **Minor logic changes:** The `find_entry_to_transmit`, `is_valid_interval`, `get_packet_txtime`, `should_change_schedules`, and the `advance_sched` refactoring, are all control flow or helper function adjustments, with lock handling. While any code change carries risk, nothing jumps out as immediately problematic from a memory initialization perspective.
4. **`parse_taprio_schedule`**: This validates, prunes and trims entries. It does check that the cycle time doesn't exceed `min_duration`. It validates each entry interval `>= min_duration`. Nothing here screams KMSAN.
5. **`setup_first_end_time`**: Uses `ktime_add_ns` and `min_t`. Standard and should be fine.
6. - 11. **`taprio_enable_offload` and `taprio_parse_tc_entries`**: These functions also use passed in `max_sdu` and `fp` values, sourced from `taprio_change`. Since these are zero-initialized arrays, the risk of uninitialized memory reads should be low. The use of function parameters also looks correct and likely reduces errors.
So, overall, after carefully going through each change, I see no obvious code path in these modifications to `net/sched/sch_taprio.c` that introduces new, KMSAN-specific, uninitialized memory risks. The key change, the `max_sdu` parameter passing, uses already zero-initialized variables, so the chance of reading uninitialized memory is low. I'd lean towards *not* setting `NeedsKMSAN=true` for this patch, unless further analysis reveals a hidden flaw. We should be fine with KASAN and the other detectors for this one.
|
| 36/3 | 2026/10/16 14:51 | tool | grepper |
0mArgs:{
"Expression": "int taprio_parse_tc_entries",
"PathPrefixes": [
"net/sched/sch_taprio.c"
]
}Results: {
"Output": "net/sched/sch_taprio.c=1924=static int taprio_parse_tc_entry(struct Qdisc *sch,\n--\nnet/sched/sch_taprio.c-1970-\nnet/sched/sch_taprio.c:1971:static int taprio_parse_tc_entries(struct Qdisc *sch,\nnet/sched/sch_taprio.c-1972-\t\t\t\t struct nlattr *opt,\n"
} |
| 37/3 | 2026/10/16 14:51 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 14149 output: 39 thoughts: 29 Thoughts: Okay, let's dive into this. Here's my take on those lines of code: **Analyzing `net/sched/sch_taprio.c` (Lines 1920-2020)** Alright, so I'm honing in on the `net/sched/sch_taprio.c` file, specifically the section around lines 1920 to 2020. This is likely where the heart of the TAPRIO scheduler's logic regarding... well, a few things I'll be looking for are likely happening. Considering TAPRIO's function, it's probably deeply involved with frame scheduling, time-aware queuing, and potentially the internal workings of how it interfaces with the hardware and the network stack in a time-sensitive way. My immediate interest is probably going to be drawn to how TAPRIO deals with: * **Interrupts/Wake-ups:** Is there a mechanism that handles generating wake-ups at specific times? * **Time Keeping:** What's the clock source and how is the timestamping handled to ensure the scheduler's precision? * **Queueing Logic:** What queues are in play, and how is the ordering of frames handled? * **Hardware Interface:** Is this section directly touching the hardware, or is it operating at a higher level of abstraction? * **Synchronization:** How does it deal with multi-threaded environments? Are there spinlocks, mutexes, or other synchronization primitives in use here? I need to understand how the scheduler avoids race conditions and data corruption. * **Error Handling:** Are there checks for exceeding timing budgets or frame sizes? How are errors handled? I anticipate that there's probably a lot of low-level time calculations, bit manipulation (for setting/reading hardware registers), and potentially direct memory access (DMA) involved. There's probably a lot of tight loops in these lines to ensure everything is operating with the required degree of determinism. I'll be looking carefully for any potential bottlenecks or inefficiencies. My goal is to determine if it is using any sophisticated algorithms to ensure the lowest latency and jitter. Specifically, if this is where the scheduling decisions are being made, I want to understand them well. Ultimately, I expect to understand how the TAPRIO scheduler is actually doing its magic to enforce those very precise timing requirements for time-sensitive networking. |
| 38/3 | 2026/10/16 14:51 | tool | read-file |
0mArgs:{
"File": "net/sched/sch_taprio.c",
"FirstLine": 1920,
"LineCount": 80
}Results: {
"Contents": "1920:\tout:\n1921:\t\treturn err;\n1922:\t}\n1923:\t\n1924:\tstatic int taprio_parse_tc_entry(struct Qdisc *sch,\n1925:\t\t\t\t\t struct nlattr *opt,\n1926:\t\t\t\t\t u32 max_sdu[TC_QOPT_MAX_QUEUE],\n1927:\t\t\t\t\t u32 fp[TC_QOPT_MAX_QUEUE],\n1928:\t\t\t\t\t unsigned long *seen_tcs,\n1929:\t\t\t\t\t struct netlink_ext_ack *extack)\n1930:\t{\n1931:\t\tstruct nlattr *tb[TCA_TAPRIO_TC_ENTRY_MAX + 1] = { };\n1932:\t\tstruct net_device *dev = qdisc_dev(sch);\n1933:\t\tint err, tc;\n1934:\t\tu32 val;\n1935:\t\n1936:\t\terr = nla_parse_nested(tb, TCA_TAPRIO_TC_ENTRY_MAX, opt,\n1937:\t\t\t\t taprio_tc_policy, extack);\n1938:\t\tif (err \u003c 0)\n1939:\t\t\treturn err;\n1940:\t\n1941:\t\tif (NL_REQ_ATTR_CHECK(extack, opt, tb, TCA_TAPRIO_TC_ENTRY_INDEX)) {\n1942:\t\t\tNL_SET_ERR_MSG_MOD(extack, \"TC entry index missing\");\n1943:\t\t\treturn -EINVAL;\n1944:\t\t}\n1945:\t\n1946:\t\ttc = nla_get_u32(tb[TCA_TAPRIO_TC_ENTRY_INDEX]);\n1947:\t\tif (*seen_tcs \u0026 BIT(tc)) {\n1948:\t\t\tNL_SET_ERR_MSG_ATTR(extack, tb[TCA_TAPRIO_TC_ENTRY_INDEX],\n1949:\t\t\t\t\t \"Duplicate tc entry\");\n1950:\t\t\treturn -EINVAL;\n1951:\t\t}\n1952:\t\n1953:\t\t*seen_tcs |= BIT(tc);\n1954:\t\n1955:\t\tif (tb[TCA_TAPRIO_TC_ENTRY_MAX_SDU]) {\n1956:\t\t\tval = nla_get_u32(tb[TCA_TAPRIO_TC_ENTRY_MAX_SDU]);\n1957:\t\t\tif (val \u003e dev-\u003emax_mtu) {\n1958:\t\t\t\tNL_SET_ERR_MSG_MOD(extack, \"TC max SDU exceeds device max MTU\");\n1959:\t\t\t\treturn -ERANGE;\n1960:\t\t\t}\n1961:\t\n1962:\t\t\tmax_sdu[tc] = val;\n1963:\t\t}\n1964:\t\n1965:\t\tif (tb[TCA_TAPRIO_TC_ENTRY_FP])\n1966:\t\t\tfp[tc] = nla_get_u32(tb[TCA_TAPRIO_TC_ENTRY_FP]);\n1967:\t\n1968:\t\treturn 0;\n1969:\t}\n1970:\t\n1971:\tstatic int taprio_parse_tc_entries(struct Qdisc *sch,\n1972:\t\t\t\t\t struct nlattr *opt,\n1973:\t\t\t\t\t u32 max_sdu[TC_MAX_QUEUE],\n1974:\t\t\t\t\t u32 fp[TC_QOPT_MAX_QUEUE],\n1975:\t\t\t\t\t struct netlink_ext_ack *extack)\n1976:\t{\n1977:\t\tstruct taprio_sched *q = qdisc_priv(sch);\n1978:\t\tstruct net_device *dev = qdisc_dev(sch);\n1979:\t\tbool have_preemption = false;\n1980:\t\tunsigned long seen_tcs = 0;\n1981:\t\tstruct nlattr *n;\n1982:\t\tint tc, rem;\n1983:\t\tint err = 0;\n1984:\t\n1985:\t\tfor (tc = 0; tc \u003c TC_QOPT_MAX_QUEUE; tc++) {\n1986:\t\t\tmax_sdu[tc] = q-\u003emax_sdu[tc];\n1987:\t\t\tfp[tc] = q-\u003efp[tc];\n1988:\t\t}\n1989:\t\n1990:\t\tnla_for_each_nested_type(n, TCA_TAPRIO_ATTR_TC_ENTRY, opt, rem) {\n1991:\t\t\terr = taprio_parse_tc_entry(sch, n, max_sdu, fp, \u0026seen_tcs,\n1992:\t\t\t\t\t\t extack);\n1993:\t\t\tif (err)\n1994:\t\t\t\treturn err;\n1995:\t\t}\n1996:\t\n1997:\t\tfor (tc = 0; tc \u003c TC_QOPT_MAX_QUEUE; tc++) {\n1998:\t\t\tif (fp[tc] != TC_FP_EXPRESS)\n1999:\t\t\t\thave_preemption = true;\n"
} |
| 39/3 | 2026/10/16 14:51 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 16046 output: 35 thoughts: 936 Thoughts: **Analyzing TAPRIO Configuration Changes**
Okay, let's break this down. I'm looking at the changes made in `taprio_change` and how it interacts with the current configuration. Initially, I zero in on `taprio_parse_tc_entries` and note that `max_sdu` and `fp` are initialized on the stack, then copied from the `q` (likely the scheduler's internal state). Then they're potentially updated based on incoming attributes. That part seems straightforward.
Now, focusing on the rest of `taprio_change`, specifically this block:
```c
struct netdev_tc_txq old_tc_to_txq[TC_MAX_QUEUE];
struct nlattr *tb[TCA_TAPRIO_ATTR_MAX + 1] = { };
u8 old_prio_tc_map[TC_BITMASK + 1];
int old_cur_txq[TC_MAX_QUEUE];
enum tk_offsets old_tk_offset;
...
if (mqprio) {
old_num_tc = dev->num_tc;
memcpy(old_tc_to_txq, dev->tc_to_txq, sizeof(old_tc_to_txq));
memcpy(old_prio_tc_map, dev->prio_tc_map,
sizeof(old_prio_tc_map));
memcpy(old_cur_txq, q->cur_txq, sizeof(old_cur_txq));
err = netdev_set_num_tc(dev, mqprio->num_tc);
if (err)
goto free_sched;
tc_configured = true;
...
free_sched:
if (err && initial_setup) {
if (tc_configured) {
WRITE_ONCE(dev->num_tc, old_num_tc);
for (i = 0; i < TC_MAX_QUEUE; i++)
WRITE_ONCE(dev->tc_to_txq[i].combined,
old_tc_to_txq[i].combined);
for (i = 0; i <= TC_BITMASK; i++)
WRITE_ONCE(dev->prio_tc_map[i],
old_prio_tc_map[i]);
memcpy(q->cur_txq, old_cur_txq, sizeof(q->cur_txq));
}
WRITE_ONCE(q->flags, old_flags);
q->clockid = old_clockid;
WRITE_ONCE(q->tk_offset, old_tk_offset);
}
```
I see that `tc_configured` is the key. It's only set if `mqprio` is true. *If* it's true, we copy a bunch of things: `old_tc_to_txq`, `old_prio_tc_map`, `old_cur_txq`, and `old_num_tc` from the device and the queue. This is essentially a save-and-restore mechanism.
* **If `tc_configured` is true** (meaning we have an mqprio change): It copies the old configuration from `dev` into local variables and updates the device's config. On error, it reverts back to the old configuration, restoring the copied state.
* **If `tc_configured` is false** (mqprio isn't set): Those `old_*` variables are *never* read, making the local copy of the `dev` state safe, as any attempt to read `old_*` would result in undefined behaviour.
* Other variables `old_flags`, `old_clockid`, and `old_tk_offset` are initialized at the beginning of the function, which will be the case no matter what the execution path.
Now, my main concern is whether this touches any data that is dumped or sent to user space. I need to know if we are leaking information or not properly handling user input. Therefore I'll search for `taprio_dump` within `net/sched/sch_taprio.c` to see if there are any related data structures or Netlink attribute generation functions modified by this patch.
|
| 40/3 | 2026/10/16 14:51 | tool | grepper |
0mArgs:{
"Expression": "taprio_dump",
"PathPrefixes": [
"net/sched/sch_taprio.c"
]
}Results: {
"Output": "net/sched/sch_taprio.c=2525=static int dump_schedule(struct sk_buff *msg,\n--\nnet/sched/sch_taprio.c-2560-\nnet/sched/sch_taprio.c:2561:static int taprio_dump_tc_entries(struct sk_buff *skb,\nnet/sched/sch_taprio.c-2562-\t\t\t\t const struct taprio_sched *q,\n--\nnet/sched/sch_taprio.c=2594=static int taprio_put_stat(struct sk_buff *skb, u64 val, u16 attrtype)\n--\nnet/sched/sch_taprio.c-2602-\nnet/sched/sch_taprio.c:2603:static int taprio_dump_xstats(struct Qdisc *sch, struct gnet_dump *d,\nnet/sched/sch_taprio.c-2604-\t\t\t struct tc_taprio_qopt_offload *offload,\n--\nnet/sched/sch_taprio.c-2650-\nnet/sched/sch_taprio.c:2651:static int taprio_dump_stats(struct Qdisc *sch, struct gnet_dump *d)\nnet/sched/sch_taprio.c-2652-{\n--\nnet/sched/sch_taprio.c-2656-\nnet/sched/sch_taprio.c:2657:\treturn taprio_dump_xstats(sch, d, \u0026offload, \u0026offload.stats);\nnet/sched/sch_taprio.c-2658-}\nnet/sched/sch_taprio.c-2659-\nnet/sched/sch_taprio.c:2660:static int taprio_dump(struct Qdisc *sch, struct sk_buff *skb)\nnet/sched/sch_taprio.c-2661-{\n--\nnet/sched/sch_taprio.c-2694-\nnet/sched/sch_taprio.c:2695:\tif (oper \u0026\u0026 taprio_dump_tc_entries(skb, q, oper))\nnet/sched/sch_taprio.c-2696-\t\tgoto options_error_rcu;\n--\nnet/sched/sch_taprio.c=2742=static unsigned long taprio_find(struct Qdisc *sch, u32 classid)\n--\nnet/sched/sch_taprio.c-2750-\nnet/sched/sch_taprio.c:2751:static int taprio_dump_class(struct Qdisc *sch, unsigned long cl,\nnet/sched/sch_taprio.c-2752-\t\t\t struct sk_buff *skb, struct tcmsg *tcm)\n--\nnet/sched/sch_taprio.c-2762-\nnet/sched/sch_taprio.c:2763:static int taprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,\nnet/sched/sch_taprio.c-2764-\t\t\t\t struct gnet_dump *d)\n--\nnet/sched/sch_taprio.c-2779-\nnet/sched/sch_taprio.c:2780:\treturn taprio_dump_xstats(sch, d, \u0026offload, \u0026offload.queue_stats.stats);\nnet/sched/sch_taprio.c-2781-}\n--\nnet/sched/sch_taprio.c=2804=static const struct Qdisc_class_ops taprio_class_ops = {\n--\nnet/sched/sch_taprio.c-2808-\t.walk\t\t= taprio_walk,\nnet/sched/sch_taprio.c:2809:\t.dump\t\t= taprio_dump_class,\nnet/sched/sch_taprio.c:2810:\t.dump_stats\t= taprio_dump_class_stats,\nnet/sched/sch_taprio.c-2811-\t.select_queue\t= taprio_select_queue,\n--\nnet/sched/sch_taprio.c=2814=static struct Qdisc_ops taprio_qdisc_ops __read_mostly = {\n--\nnet/sched/sch_taprio.c-2825-\t.enqueue\t= taprio_enqueue,\nnet/sched/sch_taprio.c:2826:\t.dump\t\t= taprio_dump,\nnet/sched/sch_taprio.c:2827:\t.dump_stats\t= taprio_dump_stats,\nnet/sched/sch_taprio.c-2828-\t.owner\t\t= THIS_MODULE,\n"
} |
| 41/3 | 2026/10/16 14:51 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19161 output: 37 thoughts: 165 Thoughts: **Analyzing the Taprio Schedule Code**
Okay, so I'm looking at this diff and the critical first observation is that none of the dump functions have been touched. That's good to know, narrows down the scope of what might be affected. Now, I need to understand how the new scheduling logic works, specifically how `parse_taprio_schedule` processes the entries. My focus now is on what changed in relation to `new_admin`, `struct sched_gate_list`, and `struct sched_entry`. Let's examine this snippet:
```c
list_for_each_entry(entry, &new->entries, list) {
if (entry->interval < min_duration) {
NL_SET_ERR_MSG(extack, "Invalid interval for schedule entry");
return -EINVAL;
}
entry->index = i++;
}
new->num_entries = i;
```
Interesting, so it iterates through a list of `entry` elements. The check for `entry->interval < min_duration` is straightforward, ensuring validity. The assignment of `entry->index` is also standard, incrementing `i`. But where are these `entry` structures coming from? That's the key question. I need to understand the allocation and initialization of these `sched_entry` structures to grasp the complete picture. Let me quickly search the codebase for where `parse_taprio_schedule` is called, or more likely, where the `sched_entry` entries are actually allocated and added to the list. This should reveal the mechanism behind the entire setup.
|
| 42/3 | 2026/10/16 14:52 | tool | grepper |
0mError:no matches Args: {
"Expression": "kzalloc.*sched_entry",
"PathPrefixes": [
"net/sched/sch_taprio.c"
]
}Results: {
"error": "no matches"
} |
| 43/3 | 2026/10/16 14:52 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19723 output: 34 thoughts: 23 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person: **Navigating the Taprio Scheduler: A Deep Dive** Alright, so the task at hand is to investigate the inner workings of the Taprio scheduler, specifically within the network stack. My immediate inclination is to pinpoint the core data structure or function representing an entry within the scheduler's queue or control mechanism. Given that, I'm going to jump right into the heart of the matter and search for `sched_entry` within `net/sched/sch_taprio.c`. This is the logical first step; focusing on this term, it's very likely to point me toward the fundamental building blocks of Taprio's scheduling logic. I expect this will reveal the data structure, function or macro that encapsulates the data of an individual scheduled transmission. From there, I can begin to unpack the underlying concepts: the timestamps, the packet-handling mechanisms, priority schemes, and eventually the complexities of how these entries are added, removed, and processed within the scheduler's overall framework. |
| 44/3 | 2026/10/16 14:53 | tool | grepper |
0mArgs:{
"Expression": "sched_entry",
"PathPrefixes": [
"net/sched/sch_taprio.c"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 223 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (1 files in total):\nnet/sched/sch_taprio.c:48\n\nnet/sched/sch_taprio.c=37=static struct static_key_false taprio_have_working_mqprio;\n--\nnet/sched/sch_taprio.c-55-\nnet/sched/sch_taprio.c:56:struct sched_entry {\nnet/sched/sch_taprio.c-57-\t/* Durations between this GCL entry and the GCL entry where the\n--\nnet/sched/sch_taprio.c=92=struct taprio_sched {\n--\nnet/sched/sch_taprio.c-106-\tspinlock_t current_entry_lock;\nnet/sched/sch_taprio.c:107:\tstruct sched_entry __rcu *current_entry;\nnet/sched/sch_taprio.c-108-\tstruct sched_gate_list __rcu *oper_sched;\n--\nnet/sched/sch_taprio.c=123=static void taprio_calculate_gate_durations(struct taprio_sched *q,\n--\nnet/sched/sch_taprio.c-127-\tint num_tc = netdev_get_num_tc(dev);\nnet/sched/sch_taprio.c:128:\tstruct sched_entry *entry, *cur;\nnet/sched/sch_taprio.c-129-\tint tc;\n--\nnet/sched/sch_taprio.c=168=static bool taprio_entry_allows_tx(ktime_t skb_end_time,\nnet/sched/sch_taprio.c:169:\t\t\t\t struct sched_entry *entry, int tc)\nnet/sched/sch_taprio.c-170-{\n--\nnet/sched/sch_taprio.c=200=static void taprio_free_sched_cb(struct rcu_head *head)\n--\nnet/sched/sch_taprio.c-202-\tstruct sched_gate_list *sched = container_of(head, struct sched_gate_list, rcu);\nnet/sched/sch_taprio.c:203:\tstruct sched_entry *entry, *n;\nnet/sched/sch_taprio.c-204-\n--\nnet/sched/sch_taprio.c=239=static ktime_t get_interval_end_time(struct sched_gate_list *sched,\nnet/sched/sch_taprio.c-240-\t\t\t\t struct sched_gate_list *admin,\nnet/sched/sch_taprio.c:241:\t\t\t\t struct sched_entry *entry,\nnet/sched/sch_taprio.c-242-\t\t\t\t ktime_t intv_start)\n--\nnet/sched/sch_taprio.c=286=static void taprio_update_queue_max_sdu(struct taprio_sched *q,\n--\nnet/sched/sch_taprio.c-338- */\nnet/sched/sch_taprio.c:339:static struct sched_entry *find_entry_to_transmit(struct sk_buff *skb,\nnet/sched/sch_taprio.c-340-\t\t\t\t\t\t struct Qdisc *sch,\n--\nnet/sched/sch_taprio.c-349-\tktime_t earliest_txtime = KTIME_MAX, txtime, cycle, transmit_end_time;\nnet/sched/sch_taprio.c:350:\tstruct sched_entry *entry = NULL, *entry_found = NULL;\nnet/sched/sch_taprio.c-351-\tstruct taprio_sched *q = qdisc_priv(sch);\n--\nnet/sched/sch_taprio.c=416=static bool is_valid_interval(struct sk_buff *skb, struct Qdisc *sch)\n--\nnet/sched/sch_taprio.c-420-\tktime_t interval_start, interval_end;\nnet/sched/sch_taprio.c:421:\tstruct sched_entry *entry;\nnet/sched/sch_taprio.c-422-\n--\nnet/sched/sch_taprio.c=446=static ktime_t get_tcp_tstamp(struct taprio_sched *q, struct sk_buff *skb)\n--\nnet/sched/sch_taprio.c-480-/* There are a few scenarios where we will have to modify the txtime from\nnet/sched/sch_taprio.c:481: * what is read from next_txtime in sched_entry. They are:\nnet/sched/sch_taprio.c-482- * 1. If txtime is in the past,\n--\nnet/sched/sch_taprio.c=495=static long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)\n--\nnet/sched/sch_taprio.c-501-\tint len, packet_transmit_time;\nnet/sched/sch_taprio.c:502:\tstruct sched_entry *entry;\nnet/sched/sch_taprio.c-503-\tbool sched_changed;\n--\nnet/sched/sch_taprio.c=690=static void taprio_set_budgets(struct taprio_sched *q,\nnet/sched/sch_taprio.c-691-\t\t\t struct sched_gate_list *sched,\nnet/sched/sch_taprio.c:692:\t\t\t struct sched_entry *entry)\nnet/sched/sch_taprio.c-693-{\n--\nnet/sched/sch_taprio.c-710-/* When an skb is sent, it consumes from the budget of all traffic classes */\nnet/sched/sch_taprio.c:711:static int taprio_update_budgets(struct sched_entry *entry, size_t len,\nnet/sched/sch_taprio.c-712-\t\t\t\t int tc_consumed, int num_tc)\n--\nnet/sched/sch_taprio.c=734=static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq,\nnet/sched/sch_taprio.c:735:\t\t\t\t\t struct sched_entry *entry,\nnet/sched/sch_taprio.c-736-\t\t\t\t\t u32 gate_mask)\n--\nnet/sched/sch_taprio.c=805=static struct sk_buff *taprio_dequeue_tc_priority(struct Qdisc *sch,\nnet/sched/sch_taprio.c:806:\t\t\t\t\t\t struct sched_entry *entry,\nnet/sched/sch_taprio.c-807-\t\t\t\t\t\t u32 gate_mask)\n--\nnet/sched/sch_taprio.c=841=static struct sk_buff *taprio_dequeue_txq_priority(struct Qdisc *sch,\nnet/sched/sch_taprio.c:842:\t\t\t\t\t\t struct sched_entry *entry,\nnet/sched/sch_taprio.c-843-\t\t\t\t\t\t u32 gate_mask)\n--\nnet/sched/sch_taprio.c=861=static struct sk_buff *taprio_dequeue(struct Qdisc *sch)\n--\nnet/sched/sch_taprio.c-864-\tstruct sk_buff *skb = NULL;\nnet/sched/sch_taprio.c:865:\tstruct sched_entry *entry;\nnet/sched/sch_taprio.c-866-\tu32 gate_mask;\n--\nnet/sched/sch_taprio.c=901=static bool should_restart_cycle(const struct sched_gate_list *oper,\nnet/sched/sch_taprio.c:902:\t\t\t\t const struct sched_entry *entry)\nnet/sched/sch_taprio.c-903-{\n--\nnet/sched/sch_taprio.c=947=static void taprio_set_entry_state(struct taprio_sched *q,\nnet/sched/sch_taprio.c-948-\t\t\t\t struct sched_gate_list *oper,\nnet/sched/sch_taprio.c:949:\t\t\t\t struct sched_entry *next,\nnet/sched/sch_taprio.c-950-\t\t\t\t ktime_t cur_start,\n--\nnet/sched/sch_taprio.c=968=static void taprio_advance_one_entry(struct taprio_sched *q,\n--\nnet/sched/sch_taprio.c-970-\t\t\t\t struct sched_gate_list **p_admin,\nnet/sched/sch_taprio.c:971:\t\t\t\t struct sched_entry **p_entry,\nnet/sched/sch_taprio.c:972:\t\t\t\t struct sched_entry **p_next,\nnet/sched/sch_taprio.c-973-\t\t\t\t ktime_t *p_end_time,\n--\nnet/sched/sch_taprio.c-977-\tstruct sched_gate_list *admin = *p_admin;\nnet/sched/sch_taprio.c:978:\tstruct sched_entry *entry = *p_entry;\nnet/sched/sch_taprio.c-979-\tktime_t cur_start, end_time;\nnet/sched/sch_taprio.c:980:\tstruct sched_entry *next;\nnet/sched/sch_taprio.c-981-\n--\nnet/sched/sch_taprio.c-986-\tif (should_restart_cycle(oper, entry)) {\nnet/sched/sch_taprio.c:987:\t\tnext = list_first_entry(\u0026oper-\u003eentries, struct sched_entry,\nnet/sched/sch_taprio.c-988-\t\t\t\t\tlist);\n--\nnet/sched/sch_taprio.c-1005-\t\t\t\t\t oper-\u003ecycle_time);\nnet/sched/sch_taprio.c:1006:\t\tnext = list_first_entry(\u0026oper-\u003eentries, struct sched_entry, list);\nnet/sched/sch_taprio.c-1007-\t\tend_time = ktime_add_ns(cur_start, next-\u003einterval);\n--\nnet/sched/sch_taprio.c=1020=static void taprio_fast_forward_sched(struct taprio_sched *q,\n--\nnet/sched/sch_taprio.c-1022-\t\t\t\t struct sched_gate_list **p_admin,\nnet/sched/sch_taprio.c:1023:\t\t\t\t struct sched_entry **p_next,\nnet/sched/sch_taprio.c-1024-\t\t\t\t ktime_t *p_end_time,\n--\nnet/sched/sch_taprio.c-1029-\tstruct sched_gate_list *admin = *p_admin;\nnet/sched/sch_taprio.c:1030:\tstruct sched_entry *entry, *next;\nnet/sched/sch_taprio.c-1031-\tktime_t end_time, cur_start;\n--\nnet/sched/sch_taprio.c-1050-\tcur_start = ktime_sub_ns(oper-\u003ecycle_end_time, oper-\u003ecycle_time);\nnet/sched/sch_taprio.c:1051:\tnext = list_last_entry(\u0026oper-\u003eentries, struct sched_entry, list);\nnet/sched/sch_taprio.c-1052-\tend_time = oper-\u003ecycle_end_time;\n--\nnet/sched/sch_taprio.c=1083=static enum hrtimer_restart advance_sched(struct hrtimer *timer)\n--\nnet/sched/sch_taprio.c-1089-\tint num_tc = netdev_get_num_tc(dev);\nnet/sched/sch_taprio.c:1090:\tstruct sched_entry *entry, *next;\nnet/sched/sch_taprio.c-1091-\tstruct Qdisc *sch = q-\u003eroot;\n--\nnet/sched/sch_taprio.c-1123-\t\t\t\t\t oper-\u003ecycle_time);\nnet/sched/sch_taprio.c:1124:\t\tnext = list_first_entry(\u0026oper-\u003eentries, struct sched_entry,\nnet/sched/sch_taprio.c-1125-\t\t\t\t\tlist);\n--\nnet/sched/sch_taprio.c-1136-\t\t\tnext = list_first_entry(\u0026oper-\u003eentries,\nnet/sched/sch_taprio.c:1137:\t\t\t\t\t\tstruct sched_entry, list);\nnet/sched/sch_taprio.c-1138-\t\t\tend_time = ktime_add_ns(cur_start, next-\u003einterval);\n--\nnet/sched/sch_taprio.c=1201=static const struct nla_policy taprio_policy[TCA_TAPRIO_ATTR_MAX + 1] = {\n--\nnet/sched/sch_taprio.c-1217-\nnet/sched/sch_taprio.c:1218:static int fill_sched_entry(struct taprio_sched *q, struct nlattr **tb,\nnet/sched/sch_taprio.c:1219:\t\t\t struct sched_entry *entry,\nnet/sched/sch_taprio.c-1220-\t\t\t struct netlink_ext_ack *extack)\n--\nnet/sched/sch_taprio.c-1249-\nnet/sched/sch_taprio.c:1250:static int parse_sched_entry(struct taprio_sched *q, struct nlattr *n,\nnet/sched/sch_taprio.c:1251:\t\t\t struct sched_entry *entry, int index,\nnet/sched/sch_taprio.c-1252-\t\t\t struct netlink_ext_ack *extack)\n--\nnet/sched/sch_taprio.c-1265-\nnet/sched/sch_taprio.c:1266:\treturn fill_sched_entry(q, tb, entry, extack);\nnet/sched/sch_taprio.c-1267-}\n--\nnet/sched/sch_taprio.c=1269=static int parse_sched_list(struct taprio_sched *q, struct nlattr *list,\n--\nnet/sched/sch_taprio.c-1280-\tnla_for_each_nested(n, list, rem) {\nnet/sched/sch_taprio.c:1281:\t\tstruct sched_entry *entry;\nnet/sched/sch_taprio.c-1282-\n--\nnet/sched/sch_taprio.c-1293-\nnet/sched/sch_taprio.c:1294:\t\terr = parse_sched_entry(q, n, entry, i, extack);\nnet/sched/sch_taprio.c-1295-\t\tif (err \u003c 0) {\n--\nnet/sched/sch_taprio.c=1309=static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,\n--\nnet/sched/sch_taprio.c-1313-\tint min_duration = taprio_min_interval(q);\nnet/sched/sch_taprio.c:1314:\tstruct sched_entry *entry, *n;\nnet/sched/sch_taprio.c-1315-\ts64 elapsed = 0;\n--\nnet/sched/sch_taprio.c-1384-\tif (elapsed \u003c new-\u003ecycle_time) {\nnet/sched/sch_taprio.c:1385:\t\tentry = list_last_entry(\u0026new-\u003eentries, struct sched_entry, list);\nnet/sched/sch_taprio.c-1386-\t\tentry-\u003einterval += new-\u003ecycle_time - elapsed;\n--\nnet/sched/sch_taprio.c=1429=static int taprio_get_start_time(struct Qdisc *sch,\n--\nnet/sched/sch_taprio.c-1446-\nnet/sched/sch_taprio.c:1447:\t/* The qdisc is expected to have at least one sched_entry. Moreover,\nnet/sched/sch_taprio.c-1448-\t * any entry must have 'interval' \u003e 0. Thus if the cycle time is zero,\n--\nnet/sched/sch_taprio.c=1463=static void setup_first_end_time(struct taprio_sched *q,\n--\nnet/sched/sch_taprio.c-1467-\tint num_tc = netdev_get_num_tc(dev);\nnet/sched/sch_taprio.c:1468:\tstruct sched_entry *first;\nnet/sched/sch_taprio.c-1469-\tktime_t cycle;\n--\nnet/sched/sch_taprio.c-1472-\tfirst = list_first_entry(\u0026sched-\u003eentries,\nnet/sched/sch_taprio.c:1473:\t\t\t\t struct sched_entry, list);\nnet/sched/sch_taprio.c-1474-\n--\nnet/sched/sch_taprio.c=1586=static void setup_txtime(struct taprio_sched *q,\n--\n\n"
} |
| 45/3 | 2026/10/16 14:53 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24357 output: 39 thoughts: 47 Thoughts: Okay, let's dive into this. Here's how I'd approach this task, walking through the logic: **Analyzing `entry` Allocation within `parse_sched_list`** My immediate concern is understanding how the `entry` variable is allocated and handled within the `parse_sched_list` function. This is critical for any detailed understanding of the Taprio scheduler's internals. I need to establish where the memory comes from and how it's managed, especially considering the potential for resource contention and errors during scheduling. The context of `parse_sched_list` suggests this `entry` is key to parsing or representing the scheduling configuration, so its proper allocation is paramount to the whole process working correctly. Therefore, I'll go directly to lines 1270-1300 of `net/sched/sch_taprio.c` to see how `entry` is declared, initialized, or otherwise manipulated within that function. I want to see how the code handles memory for `entry`, what data structures or calls are involved and if there are any potential vulnerabilities here. That's the best way to get a solid grasp of how this particular function behaves and how its use of the `entry` variable impacts the overall architecture of this section of code. |
| 46/3 | 2026/10/16 14:53 | tool | read-file |
0mArgs:{
"File": "net/sched/sch_taprio.c",
"FirstLine": 1270,
"LineCount": 40
}Results: {
"Contents": "1270:\t\t\t\t struct sched_gate_list *sched,\n1271:\t\t\t\t struct netlink_ext_ack *extack)\n1272:\t{\n1273:\t\tstruct nlattr *n;\n1274:\t\tint err, rem;\n1275:\t\tint i = 0;\n1276:\t\n1277:\t\tif (!list)\n1278:\t\t\treturn -EINVAL;\n1279:\t\n1280:\t\tnla_for_each_nested(n, list, rem) {\n1281:\t\t\tstruct sched_entry *entry;\n1282:\t\n1283:\t\t\tif (nla_type(n) != TCA_TAPRIO_SCHED_ENTRY) {\n1284:\t\t\t\tNL_SET_ERR_MSG(extack, \"Attribute is not of type 'entry'\");\n1285:\t\t\t\tcontinue;\n1286:\t\t\t}\n1287:\t\n1288:\t\t\tentry = kzalloc_obj(*entry);\n1289:\t\t\tif (!entry) {\n1290:\t\t\t\tNL_SET_ERR_MSG(extack, \"Not enough memory for entry\");\n1291:\t\t\t\treturn -ENOMEM;\n1292:\t\t\t}\n1293:\t\n1294:\t\t\terr = parse_sched_entry(q, n, entry, i, extack);\n1295:\t\t\tif (err \u003c 0) {\n1296:\t\t\t\tkfree(entry);\n1297:\t\t\t\treturn err;\n1298:\t\t\t}\n1299:\t\n1300:\t\t\tlist_add_tail(\u0026entry-\u003elist, \u0026sched-\u003eentries);\n1301:\t\t\ti++;\n1302:\t\t}\n1303:\t\n1304:\t\tsched-\u003enum_entries = i;\n1305:\t\n1306:\t\treturn i;\n1307:\t}\n1308:\t\n1309:\tstatic int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,\n"
} |
| 47/3 | 2026/10/16 14:53 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25248 output: 32 thoughts: 944 Thoughts: **Analyzing Data Structures and State Management in `taprio_change`**
Okay, so I'm looking at this `taprio_change` function and trying to get a handle on the data flow and how it handles potential errors. First, I see that the `entry` and `sched` objects, let's call it the new admin, are allocated using `kzalloc_obj`, which means they're initialized to zero. Fine.
Now, the interesting bit is around the `old_tc_to_txq`, `old_prio_tc_map`, `old_cur_txq`, and `old_num_tc` variables. These seem to be used for rollback purposes in case of an error during the Multi-Queue Priority (mqprio) configuration. I see that if `mqprio` is true, copies of the current configuration of the device's traffic classes are made into the "old" variables using `memcpy`. Then, `netdev_set_num_tc` is called to try to adjust the number of traffic classes.
If `netdev_set_num_tc` fails, the `free_sched` label is jumped to. The important part here is the conditional: `if (err && initial_setup)`. If this is the initial setup, we do the rollback with the `old_*` data. What that tells me is, if `tc_configured` is true, these "old" variables are fully and correctly initialized with the `memcpy` calls. If `tc_configured` is false, they are never read which is good. That makes the rollback logic safe.
Next, I'm checking the `max_sdu` and `fp` variables. They're initialized to zero using `= { }`. These are passed to `taprio_parse_tc_entries`, where they get overwritten by any user-provided settings from the queue structure (`q`). Then they're sent to various functions, including `taprio_update_queue_max_sdu` and `taprio_enable_offload`. Afterwards, the *modified* `max_sdu` and `fp` are written back into the queue's structures. The code flow is clear.
Wait a second. What's the relationship between `TC_MAX_QUEUE` and `TC_QOPT_MAX_QUEUE`? I'd better double-check their definitions to be sure those are not related or the intent is not to have them be the same. I'm going to pull those up immediately to avoid a potential bug here.
|
| 48/3 | 2026/10/16 14:53 | tool | grepper |
0mArgs:{
"Expression": "#define TC_MAX_QUEUE|#define TC_QOPT_MAX_QUEUE"
}Results: {
"Output": "include/linux/netdevice.h=816=struct xps_dev_maps {\n--\ninclude/linux/netdevice.h-830-\ninclude/linux/netdevice.h:831:#define TC_MAX_QUEUE\t16\ninclude/linux/netdevice.h-832-#define TC_BITMASK\t15\n--\ninclude/uapi/linux/pkt_sched.h=592=struct tc_drr_stats {\n--\ninclude/uapi/linux/pkt_sched.h-597-#define TC_QOPT_BITMASK 15\ninclude/uapi/linux/pkt_sched.h:598:#define TC_QOPT_MAX_QUEUE 16\ninclude/uapi/linux/pkt_sched.h-599-\n--\ntools/include/uapi/linux/pkt_sched.h=561=struct tc_drr_stats {\n--\ntools/include/uapi/linux/pkt_sched.h-566-#define TC_QOPT_BITMASK 15\ntools/include/uapi/linux/pkt_sched.h:567:#define TC_QOPT_MAX_QUEUE 16\ntools/include/uapi/linux/pkt_sched.h-568-\n"
} |
| 49/3 | 2026/10/16 14:53 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27087 output: 322 thoughts: 2723 Thoughts: **KMSAN Assessment: No Uninitialized Memory Read Vulnerabilities**
Okay, let's break this down. My focus here is on whether this patch introduces any uninitialized memory reads that could lead to information leaks or exploitable vulnerabilities, specifically requiring a KMSAN (Kernel Memory Sanitizer) check.
First, I've gone through each of the modified or added functions: `taprio_min_interval`, `taprio_set_entry_state`, `taprio_advance_one_entry`, `taprio_fast_forward_sched`, `advance_sched`, `parse_taprio_schedule`, `is_valid_interval`, and `get_packet_txtime`. I've carefully checked all variable declarations and assignments within these functions, looking for potential uninitialized variables, especially those used in conditional statements or calculations. I've also verified that all pointers are properly initialized before use. The key is to ensure nothing is being read from memory that hasn't been written to. My review of `advance_sched`'s logic was especially detailed, given its intricate time-based calculations and potential for errors.
I've paid close attention to how `end_time` and `next` are handled and how `taprio_advance_one_entry` and `taprio_fast_forward_sched` initializes their out parameters. This is critical in preventing unexpected behavior and potential security issues. Furthermore, I've checked `is_valid_interval`'s use of `interval_start` and `interval_end` which are written to by the call to `find_entry_to_transmit`.
Next, I've considered whether the patch copies anything to user space. It does not introduce any new `copy_to_user`, `nla_put_*`, or similar functions, nor does it alter any existing output to user space related to statistics or configuration. That's a major factor in ruling out KMSAN concerns.
Then, I focused on conditional logic. The patch does *not* introduce conditional logic or branching that relies on uninitialized variables. All local variables used in decision-making are either initialized at declaration or assigned a value before they're used in a conditional statement.
Finally, I considered memory allocation and complex data structures. All new structures and arrays are properly zero-initialized during allocation (`kzalloc_obj`) or initialization (`= { }`), which would prevent reading uninitialized memory. Moreover, rollback buffers (`old_tc_to_txq`, etc.) are protected by checks on `tc_configured` to prevent reading before `memcpy` is completed.
Based on this review, I've concluded that this patch addresses scheduling, time, and error handling issues in the `sch_taprio` code. Specifically, the patch performs:
1. Rollback of `dev->num_tc`, `tc_to_txq`, etc., on failure.
2. Defers modifications to `q->max_sdu` and `q->fp` until after possible failure points.
3. Implements hrtimer catch-up and fast-forward functionality.
4. Enforces a minimum interval to avoid interrupt storms.
5. Fixes schedule boundary comparisons and switching logic.
6. Trims schedule intervals.
These are low-level algorithmic, timing, and error-handling fixes that are not likely to cause info leaks to user space. These changes, if buggy, could result in deadlocks, memory corruption, and out-of-bounds array access. However, these risks can be detected through KASAN.
The patch does not introduce any of the conditions that would necessitate a KMSAN scan. Therefore, **NeedsKMSAN = false.**
|
| 50/3 | 2026/10/16 14:54 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch modifies net/sched/sch_taprio.c to improve schedule validation, prevent timer storms by enforcing a minimum interval and fast-forwarding laggy schedules, and cleanly roll back device/qdisc configurations on failure during taprio_change().\n\nKMSAN vs KASAN Applicability:\n- Uninitialized memory / KMSAN: All newly introduced stack arrays (`max_sdu`, `fp`) are explicitly zero-initialized (`= { }`). The rollback buffers (`old_tc_to_txq`, `old_prio_tc_map`, `old_cur_txq`) are guarded by the `tc_configured` flag and initialized via `memcpy` before use. Schedule structures are allocated using `kzalloc_obj`. No structures are copied to userspace and no uninitialized fields are read in branching or control flow. Therefore, there are no uninitialized memory usage or kernel-to-user info-leak risks.\n- KASAN / LOCKDEP / Standard detectors: The risks in this patch revolve around list handling of schedule entries, timer expiration math, locking with `q-\u003ecurrent_entry_lock`, and RCU schedule switching. Bugs in these areas (such as use-after-free, double free, deadlocks, or out-of-bounds array access) are effectively detected by KASAN and LOCKDEP.\n\nConsequently, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch modifies net/sched/sch_taprio.c to improve schedule validation, prevent timer storms by enforcing a minimum interval and fast-forwarding laggy schedules, and cleanly roll back device/qdisc configurations on failure during taprio_change().\n\nKMSAN vs KASAN Applicability:\n- Uninitialized memory / KMSAN: All newly introduced stack arrays (`max_sdu`, `fp`) are explicitly zero-initialized (`= { }`). The rollback buffers (`old_tc_to_txq`, `old_prio_tc_map`, `old_cur_txq`) are guarded by the `tc_configured` flag and initialized via `memcpy` before use. Schedule structures are allocated using `kzalloc_obj`. No structures are copied to userspace and no uninitialized fields are read in branching or control flow. Therefore, there are no uninitialized memory usage or kernel-to-user info-leak risks.\n- KASAN / LOCKDEP / Standard detectors: The risks in this patch revolve around list handling of schedule entries, timer expiration math, locking with `q-\u003ecurrent_entry_lock`, and RCU schedule switching. Bugs in these areas (such as use-after-free, double free, deadlocks, or out-of-bounds array access) are effectively detected by KASAN and LOCKDEP.\n\nConsequently, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|