mirror of
https://github.com/torvalds/linux.git
synced 2026-09-11 20:13:02 +02:00
Merge branch 'net-sched-clamp-quantum-psched_mtu-in-change-paths'
Jamal Hadi Salim says:
====================
net/sched: clamp quantum/psched_mtu in change paths
This is a followup to commit 709f34f7c2 ("net/sched: fq: add overflow
bounds to quantum and initial quantum").
The quantum_backlog_overflow series and the five siblings that followed
clamped the init-path quantum in fq, fq_codel, fq_pie, hhf, sfq. The
change() paths were not clamped but it is the same pattern, same writer
of q->quantum, same privilege level (CAP_NET_ADMIN in a user namespace).
A user can override the init clamp via tc qdisc change, restoring the
small-quantum deficit spin that the init clamp was meant to prevent.
This series also covers two siblings that were missed entirely by the
original series: sch_dualpi2 and sch_pie call psched_mtu() without any
clamp at all. With a crafted size table qdisc_pkt_len reaches ~2 GiB,
so quantum=1 (or a zero psched_mtu on a headerless device) makes the
deficit-refill loop spin ~2^31 times under the qdisc lock (a soft
lockup / denial of service).
Each patch fixes one qdisc with its own Fixes: tag so they can be
backported independently - the commits they fix shift differently in
the git tree.
Patch 1: fq - clamp TCA_FQ_QUANTUM and TCA_FQ_INITIAL_QUANTUM in change
Patch 2: fq_pie - clamp quantum in change path
Patch 3: sfq - clamp quantum and reject > 1<<20 in change path
Patch 4: hhf - clamp quantum in change and init paths
Patch 5: dualpi2 - clamp psched_mtu at all 3 call sites
Patch 6: pie - clamp psched_mtu in pie_drop_early
Patch 7: drr - clamp quantum in change class
Patch 8: ets - clamp quantum in parse and fallback paths
Patch 9: selftests - update ETS test 41f5 for clamped quanta
Conditions to recreate (applies to all): create the qdisc, then
tc qdisc change ... quantum 1 with a STAB size table inflating
qdisc_pkt_len. Requires CAP_NET_ADMIN in a user namespace (unshare -Urn).
====================
Link: https://patch.msgid.link/QDISC-0CFC.v3.20260901204856@mojatatu.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
e7c93ad4bd
|
|
@ -82,8 +82,9 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
|
|||
NL_SET_ERR_MSG(extack, "Specified DRR quantum cannot be zero");
|
||||
return -EINVAL;
|
||||
}
|
||||
quantum = clamp_t(u32, quantum, 256, 1 << 20);
|
||||
} else
|
||||
quantum = psched_mtu(qdisc_dev(sch));
|
||||
quantum = clamp_t(u32, (u32)psched_mtu(qdisc_dev(sch)), 256, 1 << 20);
|
||||
|
||||
if (cl != NULL) {
|
||||
if (tca[TCA_RATE]) {
|
||||
|
|
|
|||
|
|
@ -208,9 +208,11 @@ static void dualpi2_reset_c_protection(struct dualpi2_sched_data *q)
|
|||
static void dualpi2_calculate_c_protection(struct Qdisc *sch,
|
||||
struct dualpi2_sched_data *q, u32 wc)
|
||||
{
|
||||
u32 mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 1, 1 << 20);
|
||||
|
||||
q->c_protection_wc = wc;
|
||||
q->c_protection_wl = MAX_WC - wc;
|
||||
q->c_protection_init = (s32)psched_mtu(qdisc_dev(sch)) *
|
||||
q->c_protection_init = (s32)mtu *
|
||||
((int)q->c_protection_wc - (int)q->c_protection_wl);
|
||||
dualpi2_reset_c_protection(q);
|
||||
}
|
||||
|
|
@ -285,8 +287,9 @@ static bool must_drop(struct Qdisc *sch, struct dualpi2_sched_data *q,
|
|||
u64 local_l_prob;
|
||||
bool overload;
|
||||
u32 prob;
|
||||
u32 mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 1, 1 << 20);
|
||||
|
||||
if (sch->qstats.backlog < 2 * psched_mtu(qdisc_dev(sch)))
|
||||
if (sch->qstats.backlog < 2 * mtu)
|
||||
return false;
|
||||
|
||||
prob = READ_ONCE(q->pi2_prob);
|
||||
|
|
@ -712,7 +715,8 @@ static u32 get_memory_limit(struct Qdisc *sch, u32 limit)
|
|||
/* Apply rule of thumb, i.e., doubling the packet length,
|
||||
* to further include per packet overhead in memory_limit.
|
||||
*/
|
||||
u64 memlim = mul_u32_u32(limit, 2 * psched_mtu(qdisc_dev(sch)));
|
||||
u64 memlim = mul_u32_u32(limit, 2 * clamp_t(u32, psched_mtu(qdisc_dev(sch)),
|
||||
1, 1 << 20));
|
||||
|
||||
if (upper_32_bits(memlim))
|
||||
return U32_MAX;
|
||||
|
|
|
|||
|
|
@ -83,11 +83,7 @@ static int ets_quantum_parse(struct Qdisc *sch, const struct nlattr *attr,
|
|||
unsigned int *quantum,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
*quantum = nla_get_u32(attr);
|
||||
if (!*quantum) {
|
||||
NL_SET_ERR_MSG(extack, "ETS quantum cannot be zero");
|
||||
return -EINVAL;
|
||||
}
|
||||
*quantum = clamp_t(u32, nla_get_u32(attr), 256, 1 << 20);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -632,11 +628,13 @@ static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt,
|
|||
return err;
|
||||
}
|
||||
/* If there are more bands than strict + quanta provided, the remaining
|
||||
* ones are ETS with quantum of MTU. Initialize the missing values here.
|
||||
* ones are ETS with quantum of max(MTU, 256). Initialize the missing
|
||||
* values here.
|
||||
*/
|
||||
for (i = nstrict; i < nbands; i++) {
|
||||
if (!quanta[i])
|
||||
quanta[i] = psched_mtu(qdisc_dev(sch));
|
||||
quanta[i] = clamp_t(u32, (u32)psched_mtu(qdisc_dev(sch)),
|
||||
256, 1 << 20);
|
||||
}
|
||||
|
||||
/* Before commit, make sure we can allocate all new qdiscs */
|
||||
|
|
|
|||
|
|
@ -980,7 +980,7 @@ static int fq_resize(struct Qdisc *sch, u32 log)
|
|||
}
|
||||
|
||||
static const struct netlink_range_validation iq_range = {
|
||||
.max = INT_MAX,
|
||||
.max = 1 << 20,
|
||||
};
|
||||
|
||||
static const struct nla_policy fq_policy[TCA_FQ_MAX + 1] = {
|
||||
|
|
@ -1106,14 +1106,10 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt,
|
|||
nla_get_u32(tb[TCA_FQ_FLOW_PLIMIT]));
|
||||
|
||||
if (tb[TCA_FQ_QUANTUM]) {
|
||||
u32 quantum = nla_get_u32(tb[TCA_FQ_QUANTUM]);
|
||||
u32 quantum = clamp_t(u32, nla_get_u32(tb[TCA_FQ_QUANTUM]),
|
||||
256, 1 << 20);
|
||||
|
||||
if (quantum > 0 && quantum <= (1 << 20)) {
|
||||
WRITE_ONCE(q->quantum, quantum);
|
||||
} else {
|
||||
NL_SET_ERR_MSG_MOD(extack, "invalid quantum");
|
||||
err = -EINVAL;
|
||||
}
|
||||
WRITE_ONCE(q->quantum, quantum);
|
||||
}
|
||||
|
||||
if (tb[TCA_FQ_INITIAL_QUANTUM])
|
||||
|
|
@ -1232,7 +1228,7 @@ static int fq_init(struct Qdisc *sch, struct nlattr *opt,
|
|||
sch->limit = 10000;
|
||||
q->flow_plimit = 100;
|
||||
mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 1, 1 << 20);
|
||||
q->quantum = min_t(u32, 2 * mtu, 1 << 20);
|
||||
q->quantum = clamp_t(u32, 2 * mtu, 256, 1 << 20);
|
||||
q->initial_quantum = min_t(u32, 10 * mtu, 1 << 20);
|
||||
q->flow_refill_delay = msecs_to_jiffies(40);
|
||||
q->flow_max_rate = ~0UL;
|
||||
|
|
|
|||
|
|
@ -341,7 +341,8 @@ static int fq_pie_change(struct Qdisc *sch, struct nlattr *opt,
|
|||
nla_get_u32(tb[TCA_FQ_PIE_BETA]));
|
||||
|
||||
if (tb[TCA_FQ_PIE_QUANTUM])
|
||||
WRITE_ONCE(q->quantum, nla_get_u32(tb[TCA_FQ_PIE_QUANTUM]));
|
||||
WRITE_ONCE(q->quantum,
|
||||
max(256U, nla_get_u32(tb[TCA_FQ_PIE_QUANTUM])));
|
||||
|
||||
if (tb[TCA_FQ_PIE_MEMORY_LIMIT])
|
||||
WRITE_ONCE(q->memory_limit,
|
||||
|
|
|
|||
|
|
@ -551,7 +551,7 @@ static int hhf_change(struct Qdisc *sch, struct nlattr *opt,
|
|||
return err;
|
||||
|
||||
if (tb[TCA_HHF_QUANTUM])
|
||||
new_quantum = nla_get_u32(tb[TCA_HHF_QUANTUM]);
|
||||
new_quantum = max(256U, nla_get_u32(tb[TCA_HHF_QUANTUM]));
|
||||
|
||||
if (tb[TCA_HHF_NON_HH_WEIGHT])
|
||||
new_hhf_non_hh_weight = nla_get_u32(tb[TCA_HHF_NON_HH_WEIGHT]);
|
||||
|
|
@ -613,7 +613,7 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt,
|
|||
int i;
|
||||
|
||||
sch->limit = 1000;
|
||||
q->quantum = psched_mtu(qdisc_dev(sch));
|
||||
q->quantum = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 256, 1 << 20);
|
||||
get_random_bytes(&q->perturbation, sizeof(q->perturbation));
|
||||
INIT_LIST_HEAD(&q->new_buckets);
|
||||
INIT_LIST_HEAD(&q->old_buckets);
|
||||
|
|
@ -624,10 +624,6 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt,
|
|||
q->hhf_evict_timeout = HZ; /* 1 sec */
|
||||
q->hhf_non_hh_weight = 2;
|
||||
|
||||
if ((int)q->quantum <= 0 ||
|
||||
(u64)q->quantum * q->hhf_non_hh_weight > INT_MAX)
|
||||
q->quantum = 256;
|
||||
|
||||
if (opt) {
|
||||
int err = hhf_change(sch, opt, extack);
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ bool pie_drop_early(struct Qdisc *sch, struct pie_params *params,
|
|||
{
|
||||
u64 rnd;
|
||||
u64 local_prob = vars->prob;
|
||||
u32 mtu = psched_mtu(qdisc_dev(sch));
|
||||
u32 mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 1, 1 << 20);
|
||||
|
||||
/* If there is still burst allowance left skip random early drop */
|
||||
if (vars->burst_time > 0)
|
||||
|
|
|
|||
|
|
@ -660,6 +660,11 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ctl->quantum > 1 << 20) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "quantum too large");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ctl->perturb_period < 0 ||
|
||||
ctl->perturb_period > INT_MAX / HZ) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "invalid perturb period");
|
||||
|
|
@ -688,7 +693,7 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt,
|
|||
|
||||
/* update and validate configuration */
|
||||
if (ctl->quantum)
|
||||
quantum = ctl->quantum;
|
||||
quantum = max(256U, ctl->quantum);
|
||||
if (ctl->flows)
|
||||
maxflows = min_t(u32, ctl->flows, SFQ_MAX_FLOWS);
|
||||
if (ctl->divisor) {
|
||||
|
|
|
|||
|
|
@ -987,7 +987,7 @@
|
|||
},
|
||||
{
|
||||
"id": "41f5",
|
||||
"name": "ETS offload where the sum of quanta wraps u32",
|
||||
"name": "ETS offload with out-of-range quanta clamped",
|
||||
"category": [
|
||||
"qdisc",
|
||||
"ets"
|
||||
|
|
@ -1002,7 +1002,7 @@
|
|||
"cmdUnderTest": "$TC qdisc add dev $ETH root ets quanta 4294967294 1 1",
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC qdisc show dev $ETH",
|
||||
"matchPattern": "qdisc ets .*bands 3 quanta 4294967294 1 1",
|
||||
"matchPattern": "qdisc ets .*bands 3 quanta 1048576 256 256",
|
||||
"matchCount": "1",
|
||||
"teardown": [
|
||||
"echo \"1\" > /sys/bus/netdevsim/del_device"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user