Skip to content

Commit b0c19ed

Browse files
tohojodavem330
authored andcommitted
sch_cake: Take advantage of skb->hash where appropriate
While the other fq-based qdiscs take advantage of skb->hash and doesn't recompute it if it is already set, sch_cake does not. This was a deliberate choice because sch_cake hashes various parts of the packet header to support its advanced flow isolation modes. However, foregoing the use of skb->hash entirely loses a few important benefits: - When skb->hash is set by hardware, a few CPU cycles can be saved by not hashing again in software. - Tunnel encapsulations will generally preserve the value of skb->hash from before the encapsulation, which allows flow-based qdiscs to distinguish between flows even though the outer packet header no longer has flow information. It turns out that we can preserve these desirable properties in many cases, while still supporting the advanced flow isolation properties of sch_cake. This patch does so by reusing the skb->hash value as the flow_hash part of the hashing procedure in cake_hash() only in the following conditions: - If the skb->hash is marked as covering the flow headers (skb->l4_hash is set) AND - NAT header rewriting is either disabled, or did not change any values used for hashing. The latter is important to match local-origin packets such as those of a tunnel endpoint. The immediate motivation for fixing this was the recent patch to WireGuard to preserve the skb->hash on encapsulation. As such, this is also what I tested against; with this patch, added latency under load for competing flows drops from ~8 ms to sub-1ms on an RRUL test over a WireGuard tunnel going through a virtual link shaped to 1Gbps using sch_cake. This matches the results we saw with a similar setup using sch_fq_codel when testing the WireGuard patch. Fixes: 046f6fd ("sched: Add Common Applications Kept Enhanced (cake) qdisc") Signed-off-by: Toke Høiland-Jørgensen <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 9b23203 commit b0c19ed

File tree

1 file changed

+51
-14
lines changed

1 file changed

+51
-14
lines changed

net/sched/sch_cake.c

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -584,26 +584,48 @@ static bool cobalt_should_drop(struct cobalt_vars *vars,
584584
return drop;
585585
}
586586

587-
static void cake_update_flowkeys(struct flow_keys *keys,
587+
static bool cake_update_flowkeys(struct flow_keys *keys,
588588
const struct sk_buff *skb)
589589
{
590590
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
591591
struct nf_conntrack_tuple tuple = {};
592-
bool rev = !skb->_nfct;
592+
bool rev = !skb->_nfct, upd = false;
593+
__be32 ip;
593594

594595
if (tc_skb_protocol(skb) != htons(ETH_P_IP))
595-
return;
596+
return false;
596597

597598
if (!nf_ct_get_tuple_skb(&tuple, skb))
598-
return;
599+
return false;
599600

600-
keys->addrs.v4addrs.src = rev ? tuple.dst.u3.ip : tuple.src.u3.ip;
601-
keys->addrs.v4addrs.dst = rev ? tuple.src.u3.ip : tuple.dst.u3.ip;
601+
ip = rev ? tuple.dst.u3.ip : tuple.src.u3.ip;
602+
if (ip != keys->addrs.v4addrs.src) {
603+
keys->addrs.v4addrs.src = ip;
604+
upd = true;
605+
}
606+
ip = rev ? tuple.src.u3.ip : tuple.dst.u3.ip;
607+
if (ip != keys->addrs.v4addrs.dst) {
608+
keys->addrs.v4addrs.dst = ip;
609+
upd = true;
610+
}
602611

603612
if (keys->ports.ports) {
604-
keys->ports.src = rev ? tuple.dst.u.all : tuple.src.u.all;
605-
keys->ports.dst = rev ? tuple.src.u.all : tuple.dst.u.all;
613+
__be16 port;
614+
615+
port = rev ? tuple.dst.u.all : tuple.src.u.all;
616+
if (port != keys->ports.src) {
617+
keys->ports.src = port;
618+
upd = true;
619+
}
620+
port = rev ? tuple.src.u.all : tuple.dst.u.all;
621+
if (port != keys->ports.dst) {
622+
port = keys->ports.dst;
623+
upd = true;
624+
}
606625
}
626+
return upd;
627+
#else
628+
return false;
607629
#endif
608630
}
609631

@@ -624,23 +646,36 @@ static bool cake_ddst(int flow_mode)
624646
static u32 cake_hash(struct cake_tin_data *q, const struct sk_buff *skb,
625647
int flow_mode, u16 flow_override, u16 host_override)
626648
{
649+
bool hash_flows = (!flow_override && !!(flow_mode & CAKE_FLOW_FLOWS));
650+
bool hash_hosts = (!host_override && !!(flow_mode & CAKE_FLOW_HOSTS));
651+
bool nat_enabled = !!(flow_mode & CAKE_FLOW_NAT_FLAG);
627652
u32 flow_hash = 0, srchost_hash = 0, dsthost_hash = 0;
628653
u16 reduced_hash, srchost_idx, dsthost_idx;
629654
struct flow_keys keys, host_keys;
655+
bool use_skbhash = skb->l4_hash;
630656

631657
if (unlikely(flow_mode == CAKE_FLOW_NONE))
632658
return 0;
633659

634-
/* If both overrides are set we can skip packet dissection entirely */
635-
if ((flow_override || !(flow_mode & CAKE_FLOW_FLOWS)) &&
636-
(host_override || !(flow_mode & CAKE_FLOW_HOSTS)))
660+
/* If both overrides are set, or we can use the SKB hash and nat mode is
661+
* disabled, we can skip packet dissection entirely. If nat mode is
662+
* enabled there's another check below after doing the conntrack lookup.
663+
*/
664+
if ((!hash_flows || (use_skbhash && !nat_enabled)) && !hash_hosts)
637665
goto skip_hash;
638666

639667
skb_flow_dissect_flow_keys(skb, &keys,
640668
FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);
641669

642-
if (flow_mode & CAKE_FLOW_NAT_FLAG)
643-
cake_update_flowkeys(&keys, skb);
670+
/* Don't use the SKB hash if we change the lookup keys from conntrack */
671+
if (nat_enabled && cake_update_flowkeys(&keys, skb))
672+
use_skbhash = false;
673+
674+
/* If we can still use the SKB hash and don't need the host hash, we can
675+
* skip the rest of the hashing procedure
676+
*/
677+
if (use_skbhash && !hash_hosts)
678+
goto skip_hash;
644679

645680
/* flow_hash_from_keys() sorts the addresses by value, so we have
646681
* to preserve their order in a separate data structure to treat
@@ -679,12 +714,14 @@ static u32 cake_hash(struct cake_tin_data *q, const struct sk_buff *skb,
679714
/* This *must* be after the above switch, since as a
680715
* side-effect it sorts the src and dst addresses.
681716
*/
682-
if (flow_mode & CAKE_FLOW_FLOWS)
717+
if (hash_flows && !use_skbhash)
683718
flow_hash = flow_hash_from_keys(&keys);
684719

685720
skip_hash:
686721
if (flow_override)
687722
flow_hash = flow_override - 1;
723+
else if (use_skbhash)
724+
flow_hash = skb->hash;
688725
if (host_override) {
689726
dsthost_hash = host_override - 1;
690727
srchost_hash = host_override - 1;

0 commit comments

Comments
 (0)