]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net/sched: sch_cake: share shaper state across sub-instances of cake_mq
authorJonas Köppeler <j.koeppeler@tu-berlin.de>
Fri, 9 Jan 2026 13:15:34 +0000 (14:15 +0100)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 13 Jan 2026 10:54:29 +0000 (11:54 +0100)
This commit adds shared shaper state across the cake instances beneath a
cake_mq qdisc. It works by periodically tracking the number of active
instances, and scaling the configured rate by the number of active
queues.

The scan is lockless and simply reads the qlen and the last_active state
variable of each of the instances configured beneath the parent cake_mq
instance. Locking is not required since the values are only updated by
the owning instance, and eventual consistency is sufficient for the
purpose of estimating the number of active queues.

The interval for scanning the number of active queues is set to 200 us.
We found this to be a good tradeoff between overhead and response time.
For a detailed analysis of this aspect see the Netdevconf talk:

https://netdevconf.info/0x19/docs/netdev-0x19-paper16-talk-paper.pdf

Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: Jonas Köppeler <j.koeppeler@tu-berlin.de>
Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
Link: https://patch.msgid.link/20260109-mq-cake-sub-qdisc-v8-5-8d613fece5d8@redhat.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Documentation/netlink/specs/tc.yaml
include/uapi/linux/pkt_sched.h
net/sched/sch_cake.c

index b398f7a46dae19ba82669404e85665e56139c22e..2e663333a27982fc8d082a2ec4f856fcf1491757 100644 (file)
@@ -2207,6 +2207,9 @@ attribute-sets:
       -
         name: blue-timer-us
         type: s32
+      -
+        name: active-queues
+        type: u32
   -
     name: cake-tin-stats-attrs
     name-prefix: tca-cake-tin-stats-
index c2da76e78badebbdf7d5482cef1a3132aec99fe1..66e8072f44dffb3e71e7bd011d3e5e2958dfcc34 100644 (file)
@@ -1036,6 +1036,7 @@ enum {
        TCA_CAKE_STATS_DROP_NEXT_US,
        TCA_CAKE_STATS_P_DROP,
        TCA_CAKE_STATS_BLUE_TIMER_US,
+       TCA_CAKE_STATS_ACTIVE_QUEUES,
        __TCA_CAKE_STATS_MAX
 };
 #define TCA_CAKE_STATS_MAX (__TCA_CAKE_STATS_MAX - 1)
index 2e60e798055838d093e78f3f43f34a4dfd584bec..e30ef7f8ee6862a916acc06e568e37f35fd675b1 100644 (file)
@@ -202,6 +202,7 @@ struct cake_sched_config {
        u64             rate_bps;
        u64             interval;
        u64             target;
+       u64             sync_time;
        u32             buffer_config_limit;
        u32             fwmark_mask;
        u16             fwmark_shft;
@@ -258,6 +259,11 @@ struct cake_sched_data {
        u16             max_adjlen;
        u16             min_netlen;
        u16             min_adjlen;
+
+       /* mq sync state */
+       u64             last_checked_active;
+       u64             last_active;
+       u32             active_queues;
 };
 
 enum {
@@ -384,6 +390,8 @@ static const u32 inv_sqrt_cache[REC_INV_SQRT_CACHE] = {
        1239850263, 1191209601, 1147878294, 1108955788
 };
 
+static void cake_set_rate(struct cake_tin_data *b, u64 rate, u32 mtu,
+                         u64 target_ns, u64 rtt_est_ns);
 /* http://en.wikipedia.org/wiki/Methods_of_computing_square_roots
  * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2)
  *
@@ -2004,6 +2012,40 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
        u64 delay;
        u32 len;
 
+       if (q->config->is_shared && now - q->last_checked_active >= q->config->sync_time) {
+               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;
+               unsigned int ntx;
+
+               for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
+                       other_sch = rcu_dereference(netdev_get_tx_queue(dev, ntx)->qdisc_sleeping);
+                       other_priv = qdisc_priv(other_sch);
+
+                       if (other_priv == q)
+                               continue;
+
+                       other_qlen = READ_ONCE(other_sch->q.qlen);
+                       other_last_active = READ_ONCE(other_priv->last_active);
+
+                       if (other_qlen || other_last_active > q->last_checked_active)
+                               num_active_qs++;
+               }
+
+               if (num_active_qs > 1)
+                       new_rate = div64_u64(q->config->rate_bps, num_active_qs);
+
+               /* mtu = 0 is used to only update the rate and not mess with cobalt params */
+               cake_set_rate(b, new_rate, 0, 0, 0);
+               q->last_checked_active = now;
+               q->active_queues = num_active_qs;
+               q->rate_ns = b->tin_rate_ns;
+               q->rate_shft = b->tin_rate_shft;
+       }
+
 begin:
        if (!sch->q.qlen)
                return NULL;
@@ -2203,6 +2245,7 @@ retry:
 
        b->tin_ecn_mark += !!flow->cvars.ecn_marked;
        qdisc_bstats_update(sch, skb);
+       WRITE_ONCE(q->last_active, now);
 
        /* collect delay stats */
        delay = ktime_to_ns(ktime_sub(now, cobalt_get_enqueue_time(skb)));
@@ -2303,6 +2346,9 @@ static void cake_set_rate(struct cake_tin_data *b, u64 rate, u32 mtu,
        b->tin_rate_ns   = rate_ns;
        b->tin_rate_shft = rate_shft;
 
+       if (mtu == 0)
+               return;
+
        byte_target_ns = (byte_target * rate_ns) >> rate_shft;
 
        b->cparams.target = max((byte_target_ns * 3) / 2, target_ns);
@@ -2769,6 +2815,7 @@ static void cake_config_init(struct cake_sched_config *q, bool is_shared)
                               */
        q->rate_flags |= CAKE_FLAG_SPLIT_GSO;
        q->is_shared = is_shared;
+       q->sync_time = 200 * NSEC_PER_USEC;
 }
 
 static int cake_init(struct Qdisc *sch, struct nlattr *opt,
@@ -2842,6 +2889,9 @@ static int cake_init(struct Qdisc *sch, struct nlattr *opt,
        qd->avg_peak_bandwidth = q->rate_bps;
        qd->min_netlen = ~0;
        qd->min_adjlen = ~0;
+       qd->active_queues = 0;
+       qd->last_checked_active = 0;
+
        return 0;
 err:
        kvfree(qd->config);
@@ -2974,6 +3024,7 @@ static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
        PUT_STAT_U32(MAX_ADJLEN, q->max_adjlen);
        PUT_STAT_U32(MIN_NETLEN, q->min_netlen);
        PUT_STAT_U32(MIN_ADJLEN, q->min_adjlen);
+       PUT_STAT_U32(ACTIVE_QUEUES, q->active_queues);
 
 #undef PUT_STAT_U32
 #undef PUT_STAT_U64