public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next v2 0/5] net/sched: netem: fixes and improvements
@ 2026-05-03 19:51 Stephen Hemminger
  2026-05-03 19:51 ` [PATCH net-next v2 1/5] net/sched: netem: reorder struct netem_sched_data Stephen Hemminger
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Stephen Hemminger @ 2026-05-03 19:51 UTC (permalink / raw)
  To: netdev; +Cc: jhs, jiri, Stephen Hemminger

This is a collection of improvements to netem found while
investigating the fixes now in net tree.

v2 - incorporate AI suggestions
   - rearrange netem_sched_data
   - add multi-segment corruption improvement

Stephen Hemminger (5):
  net/sched: netem: reorder struct netem_sched_data
  net/sched: netem: remove useless VERSION
  net/sched: netem: replace pr_info with netlink extack error messages
  net/sched: netem: add per-impairment extended statistics
  net/sched: netem: handle multi-segment skb in corruption

 include/uapi/linux/pkt_sched.h |   9 ++
 net/sched/sch_netem.c          | 212 ++++++++++++++++++++-------------
 2 files changed, 136 insertions(+), 85 deletions(-)

-- 
2.53.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH net-next v2 1/5] net/sched: netem: reorder struct netem_sched_data
  2026-05-03 19:51 [PATCH net-next v2 0/5] net/sched: netem: fixes and improvements Stephen Hemminger
@ 2026-05-03 19:51 ` Stephen Hemminger
  2026-05-03 19:52 ` [PATCH net-next v2 2/5] net/sched: netem: remove useless VERSION Stephen Hemminger
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Stephen Hemminger @ 2026-05-03 19:51 UTC (permalink / raw)
  To: netdev; +Cc: jhs, jiri, Stephen Hemminger

The current layout of struct netem_sched_data can be improved
by optimizing cache locality, compacting data types (use u8
for enum) and eliminating unused elements.

Reorganize the struct as follows:

  - Cacheline 0 holds the tfifo state (t_root/t_head/t_tail/t_len),
    counter, and the unconditional enqueue scalars
    latency/jitter/rate/gap/loss.

  - Cacheline 1 holds the remaining zero-check scalars
    (duplicate/reorder/corrupt/ecn), all five crndstate correlation
    structures, and loss_model.

  - Cacheline 2 holds prng, delay_dist, the slot dequeue state,
    slot_dist, and the inner classful qdisc pointer.

  - Rate-shaping fields, q->limit (config-only; the fast path reads
    sch->limit), and the CLG Markov state move to the warm tail.

  - tc_netem_slot slot_config and qdisc_watchdog (only consulted on
    slot reschedule and watchdog wake) move to the cold tail.

Also reorder struct clgstate to place the u8 state member after the
u32 transition probabilities.  This removes the 3-byte interior hole
without changing the struct's size.

Should have no functional change.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 net/sched/sch_netem.c | 123 +++++++++++++++++++++---------------------
 1 file changed, 63 insertions(+), 60 deletions(-)

diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index bc18e1976b6e..616d33879fdc 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -71,89 +71,92 @@ struct disttable {
 	s16 table[] __counted_by(size);
 };
 
-struct netem_sched_data {
-	/* internal t(ime)fifo qdisc uses t_root and sch->limit */
-	struct rb_root t_root;
-
-	/* a linear queue; reduces rbtree rebalancing when jitter is low */
-	struct sk_buff	*t_head;
-	struct sk_buff	*t_tail;
-
-	u32 t_len;
-
-	/* optional qdisc for classful handling (NULL at netem init) */
-	struct Qdisc	*qdisc;
-
-	struct qdisc_watchdog watchdog;
+/* Loss models */
+enum {
+	CLG_RANDOM,
+	CLG_4_STATES,
+	CLG_GILB_ELL,
+};
 
-	s64 latency;
-	s64 jitter;
+/* States in GE model */
+enum {
+	GOOD_STATE = 1,
+	BAD_STATE,
+};
 
-	u32 loss;
-	u32 ecn;
-	u32 limit;
-	u32 counter;
-	u32 gap;
-	u32 duplicate;
-	u32 reorder;
-	u32 corrupt;
-	u64 rate;
-	s32 packet_overhead;
-	u32 cell_size;
-	struct reciprocal_value cell_size_reciprocal;
-	s32 cell_overhead;
+/* States in 4 state model */
+enum {
+	TX_IN_GAP_PERIOD = 1,
+	TX_IN_BURST_PERIOD,
+	LOST_IN_GAP_PERIOD,
+	LOST_IN_BURST_PERIOD,
+};
 
+struct netem_sched_data {
+	/* Cacheline 0: tfifo state and per-packet enqueue/dequeue scalars. */
+	struct rb_root		t_root;
+	struct sk_buff		*t_head;
+	struct sk_buff		*t_tail;
+	u32			t_len;
+	u32			counter;
+	s64			latency;
+	s64			jitter;
+	u64			rate;
+	u32			gap;
+	u32			loss;
+
+	/* Cacheline 1: zero-check scalars and correlation states. */
+	u32			duplicate;
+	u32			reorder;
+	u32			corrupt;
+	u32			ecn;
 	struct crndstate {
 		u32 last;
 		u32 rho;
 	} delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
+	u8			loss_model;
 
-	struct prng  {
+	/* Cacheline 2: PRNG, distribution tables, slot dequeue state etc. */
+	struct prng {
 		u64 seed;
 		struct rnd_state prng_state;
 	} prng;
+	struct disttable	*delay_dist;
+	struct slotstate {
+		u64 slot_next;
+		s32 packets_left;
+		s32 bytes_left;
+	} slot;
+	struct disttable	*slot_dist;
+	struct Qdisc		*qdisc;
 
-	struct disttable *delay_dist;
-
-	enum  {
-		CLG_RANDOM,
-		CLG_4_STATES,
-		CLG_GILB_ELL,
-	} loss_model;
-
-	enum {
-		TX_IN_GAP_PERIOD = 1,
-		TX_IN_BURST_PERIOD,
-		LOST_IN_GAP_PERIOD,
-		LOST_IN_BURST_PERIOD,
-	} _4_state_model;
-
-	enum {
-		GOOD_STATE = 1,
-		BAD_STATE,
-	} GE_state_model;
+	/*
+	 * Warm: rate-shaping parameters (only read when rate != 0) and
+	 * configuration-only fields.  The fast path reads sch->limit, not
+	 * q->limit.
+	 */
+	s32			packet_overhead;
+	u32			cell_size;
+	struct reciprocal_value	cell_size_reciprocal;
+	s32			cell_overhead;
+	u32			limit;
 
 	/* Correlated Loss Generation models */
 	struct clgstate {
-		/* state of the Markov chain */
-		u8 state;
-
 		/* 4-states and Gilbert-Elliot models */
 		u32 a1;	/* p13 for 4-states or p for GE */
 		u32 a2;	/* p31 for 4-states or r for GE */
 		u32 a3;	/* p32 for 4-states or h for GE */
 		u32 a4;	/* p14 for 4-states or 1-k for GE */
 		u32 a5; /* p23 used only in 4-states */
-	} clg;
 
-	struct tc_netem_slot slot_config;
-	struct slotstate {
-		u64 slot_next;
-		s32 packets_left;
-		s32 bytes_left;
-	} slot;
+		/* state of the Markov chain */
+		u8  state;
+	} clg;
 
-	struct disttable *slot_dist;
+	/* Cold tail: slot reschedule config and the watchdog timer. */
+	struct tc_netem_slot	slot_config;
+	struct qdisc_watchdog	watchdog;
 };
 
 /* Time stamp put into socket buffer control block
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH net-next v2 2/5] net/sched: netem: remove useless VERSION
  2026-05-03 19:51 [PATCH net-next v2 0/5] net/sched: netem: fixes and improvements Stephen Hemminger
  2026-05-03 19:51 ` [PATCH net-next v2 1/5] net/sched: netem: reorder struct netem_sched_data Stephen Hemminger
@ 2026-05-03 19:52 ` Stephen Hemminger
  2026-05-03 19:52 ` [PATCH net-next v2 3/5] net/sched: netem: replace pr_info with netlink extack error messages Stephen Hemminger
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Stephen Hemminger @ 2026-05-03 19:52 UTC (permalink / raw)
  To: netdev; +Cc: jhs, jiri, Stephen Hemminger

The version printed was never updated and kernel version is
better indication of what is fixed or not.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 net/sched/sch_netem.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 616d33879fdc..2b0b5c032e70 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -27,8 +27,6 @@
 #include <net/pkt_sched.h>
 #include <net/inet_ecn.h>
 
-#define VERSION "1.3"
-
 /*	Network Emulation Queuing algorithm.
 	====================================
 
@@ -1413,16 +1411,15 @@ static struct Qdisc_ops netem_qdisc_ops __read_mostly = {
 };
 MODULE_ALIAS_NET_SCH("netem");
 
-
 static int __init netem_module_init(void)
 {
-	pr_info("netem: version " VERSION "\n");
 	return register_qdisc(&netem_qdisc_ops);
 }
 static void __exit netem_module_exit(void)
 {
 	unregister_qdisc(&netem_qdisc_ops);
 }
+
 module_init(netem_module_init)
 module_exit(netem_module_exit)
 MODULE_LICENSE("GPL");
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH net-next v2 3/5] net/sched: netem: replace pr_info with netlink extack error messages
  2026-05-03 19:51 [PATCH net-next v2 0/5] net/sched: netem: fixes and improvements Stephen Hemminger
  2026-05-03 19:51 ` [PATCH net-next v2 1/5] net/sched: netem: reorder struct netem_sched_data Stephen Hemminger
  2026-05-03 19:52 ` [PATCH net-next v2 2/5] net/sched: netem: remove useless VERSION Stephen Hemminger
@ 2026-05-03 19:52 ` Stephen Hemminger
  2026-05-03 19:52 ` [PATCH net-next v2 4/5] net/sched: netem: add per-impairment extended statistics Stephen Hemminger
  2026-05-03 19:52 ` [PATCH net-next v2 5/5] net/sched: netem: handle multi-segment skb in corruption Stephen Hemminger
  4 siblings, 0 replies; 8+ messages in thread
From: Stephen Hemminger @ 2026-05-03 19:52 UTC (permalink / raw)
  To: netdev; +Cc: jhs, jiri, Stephen Hemminger

Use netlink extack to report errors instead of sending them
to the kernel log with pr_info(). The error message can them be seen
with tc commands; and avoids log spam.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 net/sched/sch_netem.c | 29 ++++++++++++++++-------------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 2b0b5c032e70..53961d1e70d7 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -921,7 +921,8 @@ static void get_rate(struct netem_sched_data *q, const struct nlattr *attr)
 		q->cell_size_reciprocal = (struct reciprocal_value) { 0 };
 }
 
-static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr)
+static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr,
+			struct netlink_ext_ack *extack)
 {
 	const struct nlattr *la;
 	int rem;
@@ -934,7 +935,8 @@ static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr)
 			const struct tc_netem_gimodel *gi = nla_data(la);
 
 			if (nla_len(la) < sizeof(struct tc_netem_gimodel)) {
-				pr_info("netem: incorrect gi model size\n");
+				NL_SET_ERR_MSG_ATTR(extack, la,
+						    "netem: incorrect gi model size");
 				return -EINVAL;
 			}
 
@@ -953,7 +955,8 @@ static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr)
 			const struct tc_netem_gemodel *ge = nla_data(la);
 
 			if (nla_len(la) < sizeof(struct tc_netem_gemodel)) {
-				pr_info("netem: incorrect ge model size\n");
+				NL_SET_ERR_MSG_ATTR(extack, la,
+						    "netem: incorrect ge model size");
 				return -EINVAL;
 			}
 
@@ -967,7 +970,8 @@ static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr)
 		}
 
 		default:
-			pr_info("netem: unknown loss type %u\n", type);
+			NL_SET_ERR_MSG_ATTR_FMT(extack, la,
+						"netem: unknown loss type %u", type);
 			return -EINVAL;
 		}
 	}
@@ -990,19 +994,21 @@ static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = {
 };
 
 static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla,
-		      const struct nla_policy *policy, int len)
+		      const struct nla_policy *policy, int len,
+		      struct netlink_ext_ack *extack)
 {
 	int nested_len = nla_len(nla) - NLA_ALIGN(len);
 
 	if (nested_len < 0) {
-		pr_info("netem: invalid attributes len %d\n", nested_len);
+		NL_SET_ERR_MSG_FMT(extack, "netem: invalid attributes len %u < %d",
+				   nla_len(nla), NLA_ALIGN(len));
 		return -EINVAL;
 	}
 
 	if (nested_len >= nla_attr_size(0))
 		return nla_parse_deprecated(tb, maxtype,
 					    nla_data(nla) + NLA_ALIGN(len),
-					    nested_len, policy, NULL);
+					    nested_len, policy, extack);
 
 	memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
 	return 0;
@@ -1044,8 +1050,7 @@ static int check_netem_in_tree(struct Qdisc *sch, bool duplicates,
 }
 
 /* Parse netlink message to set options */
-static int netem_change(struct Qdisc *sch, struct nlattr *opt,
-			struct netlink_ext_ack *extack)
+static int netem_change(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack)
 {
 	struct netem_sched_data *q = qdisc_priv(sch);
 	struct nlattr *tb[TCA_NETEM_MAX + 1];
@@ -1057,7 +1062,7 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt,
 	int ret;
 
 	qopt = nla_data(opt);
-	ret = parse_attr(tb, TCA_NETEM_MAX, opt, netem_policy, sizeof(*qopt));
+	ret = parse_attr(tb, TCA_NETEM_MAX, opt, netem_policy, sizeof(*qopt), extack);
 	if (ret < 0)
 		return ret;
 
@@ -1097,7 +1102,7 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt,
 	old_loss_model = q->loss_model;
 
 	if (tb[TCA_NETEM_LOSS]) {
-		ret = get_loss_clg(q, tb[TCA_NETEM_LOSS]);
+		ret = get_loss_clg(q, tb[TCA_NETEM_LOSS], extack);
 		if (ret) {
 			q->loss_model = old_loss_model;
 			q->clg = old_clg;
@@ -1193,8 +1198,6 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt,
 	prandom_seed_state(&q->prng.prng_state, q->prng.seed);
 
 	ret = netem_change(sch, opt, extack);
-	if (ret)
-		pr_info("netem: change failed\n");
 	return ret;
 }
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH net-next v2 4/5] net/sched: netem: add per-impairment extended statistics
  2026-05-03 19:51 [PATCH net-next v2 0/5] net/sched: netem: fixes and improvements Stephen Hemminger
                   ` (2 preceding siblings ...)
  2026-05-03 19:52 ` [PATCH net-next v2 3/5] net/sched: netem: replace pr_info with netlink extack error messages Stephen Hemminger
@ 2026-05-03 19:52 ` Stephen Hemminger
  2026-05-05  0:16   ` Stephen Hemminger
  2026-05-03 19:52 ` [PATCH net-next v2 5/5] net/sched: netem: handle multi-segment skb in corruption Stephen Hemminger
  4 siblings, 1 reply; 8+ messages in thread
From: Stephen Hemminger @ 2026-05-03 19:52 UTC (permalink / raw)
  To: netdev; +Cc: jhs, jiri, Stephen Hemminger

Adds new counters that keep track of when netem applied
impairments (delay, loss, corruption, duplication, reordering).
Add a struct tc_netem_xstats reported via TCA_STATS_APP so that
userspace (tc -s qdisc show) can display per-impairment counters.

Use the WRITE_ONCE/READ_ONCE pattern to allow for lockless
qdisc usage.

Accompanying iproute2 change is submitted separately.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 include/uapi/linux/pkt_sched.h |  9 ++++++
 net/sched/sch_netem.c          | 55 ++++++++++++++++++++++++++--------
 2 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h
index 66e8072f44df..1c84c8076e22 100644
--- a/include/uapi/linux/pkt_sched.h
+++ b/include/uapi/linux/pkt_sched.h
@@ -569,6 +569,15 @@ struct tc_netem_gemodel {
 #define NETEM_DIST_SCALE	8192
 #define NETEM_DIST_MAX		16384
 
+struct tc_netem_xstats {
+	__u64	delayed;	/* packets delayed */
+	__u64	dropped;	/* packets dropped by loss model      */
+	__u64	corrupted;	/* packets with bit errors injected   */
+	__u64	duplicated;	/* duplicate packets generated        */
+	__u64	reordered;	/* packets sent out of order          */
+	__u64	ecn_marked;	/* packets ECN CE-marked (not dropped)*/
+};
+
 /* DRR */
 
 enum {
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 53961d1e70d7..e710898ce96e 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -100,8 +100,7 @@ struct netem_sched_data {
 	s64			latency;
 	s64			jitter;
 	u64			rate;
-	u32			gap;
-	u32			loss;
+	u64			delayed;
 
 	/* Cacheline 1: zero-check scalars and correlation states. */
 	u32			duplicate;
@@ -112,7 +111,8 @@ struct netem_sched_data {
 		u32 last;
 		u32 rho;
 	} delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
-	u8			loss_model;
+	u32			gap;
+	u32			loss;
 
 	/* Cacheline 2: PRNG, distribution tables, slot dequeue state etc. */
 	struct prng {
@@ -125,21 +125,27 @@ struct netem_sched_data {
 		s32 packets_left;
 		s32 bytes_left;
 	} slot;
-	struct disttable	*slot_dist;
 	struct Qdisc		*qdisc;
+	u8			loss_model;
 
 	/*
-	 * Warm: rate-shaping parameters (only read when rate != 0) and
-	 * configuration-only fields.  The fast path reads sch->limit, not
-	 * q->limit.
+	 * Rare-write impairment counters (read together by netem_dump) and
+	 * rate-shaping parameters (only consulted when rate != 0).  The
+	 * fast path reads sch->limit, not q->limit.
 	 */
+	u64			dropped;
+	u64			corrupted;
+	u64			duplicated;
+	u64			ecn_marked;
+	u64			reordered;
 	s32			packet_overhead;
 	u32			cell_size;
 	struct reciprocal_value	cell_size_reciprocal;
 	s32			cell_overhead;
 	u32			limit;
 
-	/* Correlated Loss Generation models */
+	/* Cold tail: slot reschedule config and the watchdog timer. */
+	struct disttable	*slot_dist;
 	struct clgstate {
 		/* 4-states and Gilbert-Elliot models */
 		u32 a1;	/* p13 for 4-states or p for GE */
@@ -152,7 +158,6 @@ struct netem_sched_data {
 		u8  state;
 	} clg;
 
-	/* Cold tail: slot reschedule config and the watchdog timer. */
 	struct tc_netem_slot	slot_config;
 	struct qdisc_watchdog	watchdog;
 };
@@ -462,17 +467,23 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 	skb->prev = NULL;
 
 	/* Random duplication */
-	if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor, &q->prng))
+	if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor, &q->prng)) {
 		++count;
+		WRITE_ONCE(q->duplicated, q->duplicated + 1);
+	}
 
 	/* Drop packet? */
 	if (loss_event(q)) {
-		if (q->ecn && INET_ECN_set_ce(skb))
+		if (q->ecn && INET_ECN_set_ce(skb)) {
 			qdisc_qstats_drop(sch); /* mark packet */
-		else
+			WRITE_ONCE(q->ecn_marked, q->ecn_marked + 1);
+		} else {
 			--count;
+		}
 	}
+
 	if (count == 0) {
+		WRITE_ONCE(q->dropped, q->dropped + 1);
 		qdisc_qstats_drop(sch);
 		__qdisc_drop(skb, to_free);
 		return NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
@@ -498,6 +509,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 	 * do it now in software before we mangle it.
 	 */
 	if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor, &q->prng)) {
+		WRITE_ONCE(q->corrupted, q->corrupted + 1);
 		if (skb_is_gso(skb)) {
 			skb = netem_segment(skb, sch, to_free);
 			if (!skb)
@@ -603,12 +615,15 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 
 		cb->time_to_send = now + delay;
 		++q->counter;
+		WRITE_ONCE(q->delayed, q->delayed + 1);
+
 		tfifo_enqueue(skb, sch);
 	} else {
 		/*
 		 * Do re-ordering by putting one out of N packets at the front
 		 * of the queue.
 		 */
+		WRITE_ONCE(q->reordered, q->reordered + 1);
 		cb->time_to_send = ktime_get_ns();
 		q->counter = 0;
 
@@ -1348,6 +1363,21 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
 	return -1;
 }
 
+static int netem_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
+{
+	struct netem_sched_data *q = qdisc_priv(sch);
+	struct tc_netem_xstats st = {
+		.delayed    = READ_ONCE(q->delayed),
+		.dropped    = READ_ONCE(q->dropped),
+		.corrupted  = READ_ONCE(q->corrupted),
+		.duplicated = READ_ONCE(q->duplicated),
+		.reordered  = READ_ONCE(q->reordered),
+		.ecn_marked = READ_ONCE(q->ecn_marked),
+	};
+
+	return gnet_stats_copy_app(d, &st, sizeof(st));
+}
+
 static int netem_dump_class(struct Qdisc *sch, unsigned long cl,
 			  struct sk_buff *skb, struct tcmsg *tcm)
 {
@@ -1410,6 +1440,7 @@ static struct Qdisc_ops netem_qdisc_ops __read_mostly = {
 	.destroy	=	netem_destroy,
 	.change		=	netem_change,
 	.dump		=	netem_dump,
+	.dump_stats	=	netem_dump_stats,
 	.owner		=	THIS_MODULE,
 };
 MODULE_ALIAS_NET_SCH("netem");
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH net-next v2 5/5] net/sched: netem: handle multi-segment skb in corruption
  2026-05-03 19:51 [PATCH net-next v2 0/5] net/sched: netem: fixes and improvements Stephen Hemminger
                   ` (3 preceding siblings ...)
  2026-05-03 19:52 ` [PATCH net-next v2 4/5] net/sched: netem: add per-impairment extended statistics Stephen Hemminger
@ 2026-05-03 19:52 ` Stephen Hemminger
  2026-05-05  0:25   ` Stephen Hemminger
  4 siblings, 1 reply; 8+ messages in thread
From: Stephen Hemminger @ 2026-05-03 19:52 UTC (permalink / raw)
  To: netdev; +Cc: jhs, jiri, Stephen Hemminger

The packet corruption code only flipped bits in the linear
header portion of the skb, skipping corruption when
skb_headlen() was zero.

Use skb_header_pointer() and skb_store_bits() to access the
full packet data, allowing any bit in the packet to be
corrupted regardless of how the skb is laid out.

Replaces d64cb81dcbd5 ("net/sched: sch_netem: fix out-of-bounds access
in packet corruption") with a more general solution.

Only count the number of packets that were actually corrupted.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 net/sched/sch_netem.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index e710898ce96e..5cbd1a0dbfda 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -509,7 +509,6 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 	 * do it now in software before we mangle it.
 	 */
 	if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor, &q->prng)) {
-		WRITE_ONCE(q->corrupted, q->corrupted + 1);
 		if (skb_is_gso(skb)) {
 			skb = netem_segment(skb, sch, to_free);
 			if (!skb)
@@ -532,9 +531,18 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 			goto finish_segs;
 		}
 
-		if (skb_headlen(skb))
-			skb->data[get_random_u32_below(skb_headlen(skb))] ^=
-				1 << get_random_u32_below(8);
+		if (skb->len > 0) {
+			unsigned int offset = get_random_u32_below(skb->len);
+			u8 *ptr, val;
+
+			/* handle multi-segment skb's */
+			ptr = skb_header_pointer(skb, offset, 1, &val);
+			if (ptr) {
+				val = *ptr ^ (1 << get_random_u32_below(8));
+				skb_store_bits(skb, offset, &val, 1);
+				WRITE_ONCE(q->corrupted, q->corrupted + 1);
+			}
+		}
 	}
 
 	if (unlikely(sch->q.qlen >= sch->limit)) {
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH net-next v2 4/5] net/sched: netem: add per-impairment extended statistics
  2026-05-03 19:52 ` [PATCH net-next v2 4/5] net/sched: netem: add per-impairment extended statistics Stephen Hemminger
@ 2026-05-05  0:16   ` Stephen Hemminger
  0 siblings, 0 replies; 8+ messages in thread
From: Stephen Hemminger @ 2026-05-05  0:16 UTC (permalink / raw)
  To: netdev; +Cc: jhs, jiri

On Sun,  3 May 2026 12:52:02 -0700
Stephen Hemminger <stephen@networkplumber.org> wrote:

> Adds new counters that keep track of when netem applied
> impairments (delay, loss, corruption, duplication, reordering).
> Add a struct tc_netem_xstats reported via TCA_STATS_APP so that
> userspace (tc -s qdisc show) can display per-impairment counters.
> 
> Use the WRITE_ONCE/READ_ONCE pattern to allow for lockless
> qdisc usage.
> 
> Accompanying iproute2 change is submitted separately.
> 
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> ---

I don't know how to argue with Sashiko but it is wrong.
It flags the counters as susceptible to torn reads but these
are informational only counters and the new counters follow the
pattern of Eric's other recent changes.

False positive report.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH net-next v2 5/5] net/sched: netem: handle multi-segment skb in corruption
  2026-05-03 19:52 ` [PATCH net-next v2 5/5] net/sched: netem: handle multi-segment skb in corruption Stephen Hemminger
@ 2026-05-05  0:25   ` Stephen Hemminger
  0 siblings, 0 replies; 8+ messages in thread
From: Stephen Hemminger @ 2026-05-05  0:25 UTC (permalink / raw)
  To: netdev; +Cc: jhs, jiri

On Sun,  3 May 2026 12:52:03 -0700
Stephen Hemminger <stephen@networkplumber.org> wrote:

> The packet corruption code only flipped bits in the linear
> header portion of the skb, skipping corruption when
> skb_headlen() was zero.
> 
> Use skb_header_pointer() and skb_store_bits() to access the
> full packet data, allowing any bit in the packet to be
> corrupted regardless of how the skb is laid out.
> 
> Replaces d64cb81dcbd5 ("net/sched: sch_netem: fix out-of-bounds access
> in packet corruption") with a more general solution.
> 
> Only count the number of packets that were actually corrupted.
> 
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> ---

There are problems with this since it doesn't handle zero copy 
correctly. Needs to make full copy.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-05-05  0:25 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-03 19:51 [PATCH net-next v2 0/5] net/sched: netem: fixes and improvements Stephen Hemminger
2026-05-03 19:51 ` [PATCH net-next v2 1/5] net/sched: netem: reorder struct netem_sched_data Stephen Hemminger
2026-05-03 19:52 ` [PATCH net-next v2 2/5] net/sched: netem: remove useless VERSION Stephen Hemminger
2026-05-03 19:52 ` [PATCH net-next v2 3/5] net/sched: netem: replace pr_info with netlink extack error messages Stephen Hemminger
2026-05-03 19:52 ` [PATCH net-next v2 4/5] net/sched: netem: add per-impairment extended statistics Stephen Hemminger
2026-05-05  0:16   ` Stephen Hemminger
2026-05-03 19:52 ` [PATCH net-next v2 5/5] net/sched: netem: handle multi-segment skb in corruption Stephen Hemminger
2026-05-05  0:25   ` Stephen Hemminger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox