* [PATCH net-next 1/4] net/sched: netem: move state enums out of struct netem_sched_data
[not found] <20260502001844.19107-1-stephen@networkplumber.org>
@ 2026-05-02 0:17 ` Stephen Hemminger
2026-05-02 0:17 ` [PATCH net-next 2/4] net/sched: netem: remove useless VERSION Stephen Hemminger
` (2 subsequent siblings)
3 siblings, 0 replies; 4+ messages in thread
From: Stephen Hemminger @ 2026-05-02 0:17 UTC (permalink / raw)
To: netdev
Cc: jhs, jiri, Stephen Hemminger, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman,
open list:NETEM NETWORK EMULATOR, open list
The _4_state_model and GE_state_model enum definitions are declared
as struct members but are never read or written. Only the enum
constants they define (TX_IN_GAP_PERIOD, GOOD_STATE, etc.) are used.
Move them to file scope as anonymous enums and remove the unused
struct fields, saving 8 bytes per netem instance.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index bc18e1976b6e..148b22fb3ca3 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -71,6 +71,18 @@ struct disttable {
s16 table[] __counted_by(size);
};
+enum GE_state_model {
+ GOOD_STATE = 1,
+ BAD_STATE,
+};
+
+enum _4_state_model {
+ TX_IN_GAP_PERIOD = 1,
+ TX_IN_BURST_PERIOD,
+ LOST_IN_GAP_PERIOD,
+ LOST_IN_BURST_PERIOD,
+};
+
struct netem_sched_data {
/* internal t(ime)fifo qdisc uses t_root and sch->limit */
struct rb_root t_root;
@@ -121,18 +133,6 @@ struct netem_sched_data {
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;
-
/* Correlated Loss Generation models */
struct clgstate {
/* state of the Markov chain */
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH net-next 2/4] net/sched: netem: remove useless VERSION
[not found] <20260502001844.19107-1-stephen@networkplumber.org>
2026-05-02 0:17 ` [PATCH net-next 1/4] net/sched: netem: move state enums out of struct netem_sched_data Stephen Hemminger
@ 2026-05-02 0:17 ` Stephen Hemminger
2026-05-02 0:17 ` [PATCH net-next 3/4] net/sched: netem: replace pr_info with netlink extack error messages Stephen Hemminger
2026-05-02 0:17 ` [PATCH net-next 4/4] netem: add per-impairment extended statistics Stephen Hemminger
3 siblings, 0 replies; 4+ messages in thread
From: Stephen Hemminger @ 2026-05-02 0:17 UTC (permalink / raw)
To: netdev
Cc: jhs, jiri, Stephen Hemminger, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman,
open list:NETEM NETWORK EMULATOR, open list
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 148b22fb3ca3..9ce75d9b8c54 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.
====================================
@@ -1410,16 +1408,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] 4+ messages in thread
* [PATCH net-next 3/4] net/sched: netem: replace pr_info with netlink extack error messages
[not found] <20260502001844.19107-1-stephen@networkplumber.org>
2026-05-02 0:17 ` [PATCH net-next 1/4] net/sched: netem: move state enums out of struct netem_sched_data Stephen Hemminger
2026-05-02 0:17 ` [PATCH net-next 2/4] net/sched: netem: remove useless VERSION Stephen Hemminger
@ 2026-05-02 0:17 ` Stephen Hemminger
2026-05-02 0:17 ` [PATCH net-next 4/4] netem: add per-impairment extended statistics Stephen Hemminger
3 siblings, 0 replies; 4+ messages in thread
From: Stephen Hemminger @ 2026-05-02 0:17 UTC (permalink / raw)
To: netdev
Cc: jhs, jiri, Stephen Hemminger, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman,
open list:NETEM NETWORK EMULATOR, open list
netem predates the netlink extended ack mechanism and uses pr_info()
to report configuration errors. These messages go to the kernel log
where the user running tc may never see them, and in unprivileged
user namespace contexts they can be used for log spam.
Replace pr_info() with NL_SET_ERR_MSG() and NL_SET_ERR_MSG_FMT()
which return error details to the caller via netlink. Thread the
extack pointer through parse_attr() and get_loss_clg(). Remove
the uninformative "netem: change failed" message from netem_init()
since the error is already propagated.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 9ce75d9b8c54..116e96d79ddf 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -918,7 +918,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;
@@ -931,7 +932,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(extack,
+ "netem: incorrect gi model size");
return -EINVAL;
}
@@ -950,7 +952,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(extack,
+ "netem: incorrect ge model size");
return -EINVAL;
}
@@ -964,7 +967,7 @@ 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_FMT(extack, "netem: unknown loss type %u", type);
return -EINVAL;
}
}
@@ -987,12 +990,14 @@ 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, size_t 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 < %zu",
+ nla_len(nla), NLA_ALIGN(len));
return -EINVAL;
}
@@ -1041,8 +1046,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];
@@ -1054,7 +1058,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;
@@ -1094,7 +1098,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;
@@ -1190,8 +1194,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] 4+ messages in thread
* [PATCH net-next 4/4] netem: add per-impairment extended statistics
[not found] <20260502001844.19107-1-stephen@networkplumber.org>
` (2 preceding siblings ...)
2026-05-02 0:17 ` [PATCH net-next 3/4] net/sched: netem: replace pr_info with netlink extack error messages Stephen Hemminger
@ 2026-05-02 0:17 ` Stephen Hemminger
3 siblings, 0 replies; 4+ messages in thread
From: Stephen Hemminger @ 2026-05-02 0:17 UTC (permalink / raw)
To: netdev
Cc: jhs, jiri, Stephen Hemminger, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, open list:TC subsystem,
open list
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 | 40 +++++++++++++++++++++++++++++++---
2 files changed, 46 insertions(+), 3 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 116e96d79ddf..c871c7a3b117 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -152,6 +152,14 @@ struct netem_sched_data {
} slot;
struct disttable *slot_dist;
+
+ /* Per-impairment counters */
+ __u64 delayed;
+ __u64 dropped;
+ __u64 corrupted;
+ __u64 duplicated;
+ __u64 reordered;
+ __u64 ecn_marked;
};
/* Time stamp put into socket buffer control block
@@ -459,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;
@@ -495,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)
@@ -600,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;
@@ -1344,6 +1362,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)
{
@@ -1406,6 +1439,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] 4+ messages in thread
end of thread, other threads:[~2026-05-02 0:18 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260502001844.19107-1-stephen@networkplumber.org>
2026-05-02 0:17 ` [PATCH net-next 1/4] net/sched: netem: move state enums out of struct netem_sched_data Stephen Hemminger
2026-05-02 0:17 ` [PATCH net-next 2/4] net/sched: netem: remove useless VERSION Stephen Hemminger
2026-05-02 0:17 ` [PATCH net-next 3/4] net/sched: netem: replace pr_info with netlink extack error messages Stephen Hemminger
2026-05-02 0:17 ` [PATCH net-next 4/4] netem: add per-impairment extended statistics Stephen Hemminger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox