mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
Merge branch 'net-sched-sch_cake-fixes-for-accounting-and-reconfiguration'
Jamal Hadi Salim says: ==================== net/sched: sch_cake: fixes for accounting and reconfiguration Four independent fixes in the CAKE qdisc. Three are follow-ups to the autorate reconfiguration work around commit7cbfb18094("net/sched: sch_cake: fix autorate reconfiguration throttling") and the 32-bit admission-counter wraps fixed in gred/bfifo/plug by commit4c660ee8c8("net: sched: fix 32-bit backlog wrap in gred, bfifo and plug enqueue"); the fourth is a dead-code cleanup found while auditing the dump path. Patch 1 serializes a 'tc qdisc change' with the datapath's autorate update under the qdisc lock, so the netlink writer and the autorate writer can no longer lose the configured rate. rate_bps needs no atomic (it is only loaded and stored), so it stays a plain u64 with READ_ONCE()/WRITE_ONCE() annotations. Patch 2 propagates the backlog reduction of the reconfigure-time tin purge to classful parents, so a CAKE leaf under HTB no longer leaves the purged packets in the parent's qlen/backlog forever. Patch 3 widens buffer_used and buffer_max_used to u64. The u32 accumulator wraps once the resident truesize passes 2^32, bypassing the over-limit drop loop while the configured limit is a u32 attribute; the u32 MEMORY_USED export is clamped so widening the counter does not truncate the reported value. Patch 4 drops an unreachable 'nla_nest_end() < 0' error check: the helper never returns a negative value. All four are hardening for net-next (no Fixes:, no Cc: stable): the counter wrap, the missing parent-backlog propagation and the reconfiguration race all predate any contract we could anchor a regression on. Patches 1-2 were reported by Sashiko (gemini) and patch 3 comes from the gred 32-bit-wrap family; patch 4 is a static cleanup. ==================== Link: https://patch.msgid.link/QDISC-5RTM.v3.r0.20261020053711@mojatatu.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
82858f249d
|
|
@ -63,6 +63,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/reciprocal_div.h>
|
||||
#include <linux/u64_stats_sync.h>
|
||||
#include <net/netlink.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <net/gso.h>
|
||||
|
|
@ -234,8 +235,10 @@ struct cake_sched_data {
|
|||
u16 tin_cnt;
|
||||
|
||||
/* resource tracking */
|
||||
u32 buffer_used;
|
||||
u32 buffer_max_used;
|
||||
|
||||
struct u64_stats_sync buffer_max_used_sync;
|
||||
u64_stats_t buffer_max_used;
|
||||
u64 buffer_used;
|
||||
u32 buffer_limit;
|
||||
|
||||
/* indices for dequeue */
|
||||
|
|
@ -1850,7 +1853,8 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
|
|||
qdisc_qstats_drop(sch);
|
||||
ack_pkt_len = qdisc_pkt_len(ack);
|
||||
WRITE_ONCE(b->bytes, b->bytes + ack_pkt_len);
|
||||
q->buffer_used += skb->truesize - ack->truesize;
|
||||
q->buffer_used += skb->truesize;
|
||||
q->buffer_used -= ack->truesize;
|
||||
if (q->config->rate_flags & CAKE_FLAG_INGRESS)
|
||||
cake_advance_shaper(q, b, ack, now, true);
|
||||
|
||||
|
|
@ -1906,7 +1910,8 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
|
|||
if (ktime_after(now,
|
||||
ktime_add_ms(q->last_reconfig_time,
|
||||
250))) {
|
||||
q->config->rate_bps = (q->avg_peak_bandwidth * 15) >> 4;
|
||||
WRITE_ONCE(q->config->rate_bps,
|
||||
(q->avg_peak_bandwidth * 15) >> 4);
|
||||
q->last_reconfig_time = now;
|
||||
cake_reconfigure(sch);
|
||||
}
|
||||
|
|
@ -1940,8 +1945,11 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
|
|||
cake_inc_dsthost_bulk_flow_count(b, flow, q->config->flow_mode);
|
||||
}
|
||||
|
||||
if (q->buffer_used > q->buffer_max_used)
|
||||
WRITE_ONCE(q->buffer_max_used, q->buffer_used);
|
||||
if (q->buffer_used > u64_stats_read(&q->buffer_max_used)) {
|
||||
u64_stats_update_begin(&q->buffer_max_used_sync);
|
||||
u64_stats_set(&q->buffer_max_used, q->buffer_used);
|
||||
u64_stats_update_end(&q->buffer_max_used_sync);
|
||||
}
|
||||
|
||||
if (q->buffer_used <= q->buffer_limit)
|
||||
return NET_XMIT_SUCCESS;
|
||||
|
|
@ -2019,9 +2027,9 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
|
|||
|
||||
if (q->config->is_shared && q->rate_ns &&
|
||||
now - q->last_checked_active >= q->config->sync_time) {
|
||||
u64 new_rate = READ_ONCE(q->config->rate_bps);
|
||||
struct net_device *dev = qdisc_dev(sch);
|
||||
struct cake_sched_data *other_priv;
|
||||
u64 new_rate = q->config->rate_bps;
|
||||
u64 other_qlen, other_last_active;
|
||||
struct Qdisc *other_sch;
|
||||
u32 num_active_qs = 1;
|
||||
|
|
@ -2042,7 +2050,7 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
|
|||
}
|
||||
|
||||
if (num_active_qs > 1)
|
||||
new_rate = div64_u64(q->config->rate_bps, num_active_qs);
|
||||
new_rate = div64_u64(new_rate, num_active_qs);
|
||||
|
||||
cake_configure_rates(sch, new_rate, true);
|
||||
q->last_checked_active = now;
|
||||
|
|
@ -2610,10 +2618,17 @@ static void cake_configure_rates(struct Qdisc *sch, u64 rate, bool rate_adjust)
|
|||
}
|
||||
|
||||
if (!rate_adjust) {
|
||||
u32 backlog = sch->qstats.backlog, qlen = sch->q.qlen;
|
||||
|
||||
for (c = qd->tin_cnt; c < CAKE_MAX_TINS; c++) {
|
||||
cake_clear_tin(sch, c);
|
||||
qd->tins[c].cparams.mtu_time = qd->tins[ft].cparams.mtu_time;
|
||||
}
|
||||
|
||||
backlog -= sch->qstats.backlog;
|
||||
qlen -= sch->q.qlen;
|
||||
if (qlen)
|
||||
qdisc_tree_reduce_backlog(sch, qlen, backlog);
|
||||
}
|
||||
|
||||
qd->rate_ns = qd->tins[ft].tin_rate_ns;
|
||||
|
|
@ -2625,13 +2640,15 @@ static void cake_reconfigure(struct Qdisc *sch)
|
|||
struct cake_sched_data *qd = qdisc_priv(sch);
|
||||
struct cake_sched_config *q = qd->config;
|
||||
u32 buffer_limit;
|
||||
u64 rate_bps;
|
||||
|
||||
cake_configure_rates(sch, qd->config->rate_bps, false);
|
||||
rate_bps = READ_ONCE(qd->config->rate_bps);
|
||||
cake_configure_rates(sch, rate_bps, false);
|
||||
|
||||
if (q->buffer_config_limit) {
|
||||
buffer_limit = q->buffer_config_limit;
|
||||
} else if (q->rate_bps) {
|
||||
u64 t = q->rate_bps * q->interval;
|
||||
} else if (rate_bps) {
|
||||
u64 t = rate_bps * q->interval;
|
||||
|
||||
do_div(t, USEC_PER_SEC / 4);
|
||||
buffer_limit = max_t(u32, t, 4U << 20);
|
||||
|
|
@ -2784,9 +2801,16 @@ static int cake_change(struct Qdisc *sch, struct nlattr *opt,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Once the qdisc is live, commit and reconfigure under the lock that
|
||||
* serializes the datapath, so a concurrent 'tc qdisc change' and the
|
||||
* autorate update cannot lose the configured rate.
|
||||
*/
|
||||
if (qd->tins)
|
||||
sch_tree_lock(sch);
|
||||
|
||||
ret = cake_config_change(q, opt, extack, &overhead_changed);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto unlock;
|
||||
|
||||
if (overhead_changed) {
|
||||
WRITE_ONCE(qd->max_netlen, 0);
|
||||
|
|
@ -2795,13 +2819,13 @@ static int cake_change(struct Qdisc *sch, struct nlattr *opt,
|
|||
WRITE_ONCE(qd->min_adjlen, ~0);
|
||||
}
|
||||
|
||||
if (qd->tins) {
|
||||
sch_tree_lock(sch);
|
||||
if (qd->tins)
|
||||
cake_reconfigure(sch);
|
||||
unlock:
|
||||
if (qd->tins)
|
||||
sch_tree_unlock(sch);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cake_destroy(struct Qdisc *sch)
|
||||
|
|
@ -2845,6 +2869,7 @@ static int cake_init(struct Qdisc *sch, struct nlattr *opt,
|
|||
qd->cur_flow = 0;
|
||||
qd->config = q;
|
||||
|
||||
u64_stats_init(&qd->buffer_max_used_sync);
|
||||
qdisc_watchdog_init(&qd->watchdog, sch);
|
||||
|
||||
if (opt) {
|
||||
|
|
@ -2995,6 +3020,8 @@ static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
|
|||
struct nlattr *stats = nla_nest_start_noflag(d->skb, TCA_STATS_APP);
|
||||
struct cake_sched_data *q = qdisc_priv(sch);
|
||||
struct nlattr *tstats, *ts;
|
||||
u64 buffer_max_used;
|
||||
unsigned int start;
|
||||
int i;
|
||||
|
||||
if (!stats)
|
||||
|
|
@ -3010,9 +3037,14 @@ static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
|
|||
goto nla_put_failure; \
|
||||
} while (0)
|
||||
|
||||
do {
|
||||
start = u64_stats_fetch_begin(&q->buffer_max_used_sync);
|
||||
buffer_max_used = u64_stats_read(&q->buffer_max_used);
|
||||
} while (u64_stats_fetch_retry(&q->buffer_max_used_sync, start));
|
||||
|
||||
PUT_STAT_U64(CAPACITY_ESTIMATE64, READ_ONCE(q->avg_peak_bandwidth));
|
||||
PUT_STAT_U32(MEMORY_LIMIT, READ_ONCE(q->buffer_limit));
|
||||
PUT_STAT_U32(MEMORY_USED, READ_ONCE(q->buffer_max_used));
|
||||
PUT_STAT_U32(MEMORY_USED, min_t(u64, buffer_max_used, U32_MAX));
|
||||
PUT_STAT_U32(AVG_NETOFF, ((READ_ONCE(q->avg_netoff) + 0x8000) >> 16));
|
||||
PUT_STAT_U32(MAX_NETLEN, READ_ONCE(q->max_netlen));
|
||||
PUT_STAT_U32(MAX_ADJLEN, READ_ONCE(q->max_adjlen));
|
||||
|
|
@ -3194,8 +3226,7 @@ static int cake_dump_class_stats(struct Qdisc *sch, unsigned long cl,
|
|||
READ_ONCE(flow->cvars.drop_next))));
|
||||
}
|
||||
|
||||
if (nla_nest_end(d->skb, stats) < 0)
|
||||
return -1;
|
||||
nla_nest_end(d->skb, stats);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user