diff options
Diffstat (limited to 'net/sched/sch_cake.c')
| -rw-r--r-- | net/sched/sch_cake.c | 62 | 
1 files changed, 43 insertions, 19 deletions
diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index 60f8ae578819..ebaeec1e5c82 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -592,7 +592,7 @@ static bool cake_update_flowkeys(struct flow_keys *keys,  	bool rev = !skb->_nfct, upd = false;  	__be32 ip; -	if (tc_skb_protocol(skb) != htons(ETH_P_IP)) +	if (skb_protocol(skb, true) != htons(ETH_P_IP))  		return false;  	if (!nf_ct_get_tuple_skb(&tuple, skb)) @@ -1551,32 +1551,51 @@ static unsigned int cake_drop(struct Qdisc *sch, struct sk_buff **to_free)  	return idx + (tin << 16);  } -static u8 cake_handle_diffserv(struct sk_buff *skb, u16 wash) +static u8 cake_handle_diffserv(struct sk_buff *skb, bool wash)  { -	int wlen = skb_network_offset(skb); +	const int offset = skb_network_offset(skb); +	u16 *buf, buf_;  	u8 dscp; -	switch (tc_skb_protocol(skb)) { +	switch (skb_protocol(skb, true)) {  	case htons(ETH_P_IP): -		wlen += sizeof(struct iphdr); -		if (!pskb_may_pull(skb, wlen) || -		    skb_try_make_writable(skb, wlen)) +		buf = skb_header_pointer(skb, offset, sizeof(buf_), &buf_); +		if (unlikely(!buf))  			return 0; -		dscp = ipv4_get_dsfield(ip_hdr(skb)) >> 2; -		if (wash && dscp) +		/* ToS is in the second byte of iphdr */ +		dscp = ipv4_get_dsfield((struct iphdr *)buf) >> 2; + +		if (wash && dscp) { +			const int wlen = offset + sizeof(struct iphdr); + +			if (!pskb_may_pull(skb, wlen) || +			    skb_try_make_writable(skb, wlen)) +				return 0; +  			ipv4_change_dsfield(ip_hdr(skb), INET_ECN_MASK, 0); +		} +  		return dscp;  	case htons(ETH_P_IPV6): -		wlen += sizeof(struct ipv6hdr); -		if (!pskb_may_pull(skb, wlen) || -		    skb_try_make_writable(skb, wlen)) +		buf = skb_header_pointer(skb, offset, sizeof(buf_), &buf_); +		if (unlikely(!buf))  			return 0; -		dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2; -		if (wash && dscp) +		/* Traffic class is in the first and second bytes of ipv6hdr */ +		dscp = ipv6_get_dsfield((struct ipv6hdr *)buf) >> 2; + +		if (wash && dscp) { +			const int wlen = offset + sizeof(struct ipv6hdr); + +			if (!pskb_may_pull(skb, wlen) || +			    skb_try_make_writable(skb, wlen)) +				return 0; +  			ipv6_change_dsfield(ipv6_hdr(skb), INET_ECN_MASK, 0); +		} +  		return dscp;  	case htons(ETH_P_ARP): @@ -1593,14 +1612,17 @@ static struct cake_tin_data *cake_select_tin(struct Qdisc *sch,  {  	struct cake_sched_data *q = qdisc_priv(sch);  	u32 tin, mark; +	bool wash;  	u8 dscp;  	/* Tin selection: Default to diffserv-based selection, allow overriding -	 * using firewall marks or skb->priority. +	 * using firewall marks or skb->priority. Call DSCP parsing early if +	 * wash is enabled, otherwise defer to below to skip unneeded parsing.  	 */ -	dscp = cake_handle_diffserv(skb, -				    q->rate_flags & CAKE_FLAG_WASH);  	mark = (skb->mark & q->fwmark_mask) >> q->fwmark_shft; +	wash = !!(q->rate_flags & CAKE_FLAG_WASH); +	if (wash) +		dscp = cake_handle_diffserv(skb, wash);  	if (q->tin_mode == CAKE_DIFFSERV_BESTEFFORT)  		tin = 0; @@ -1614,6 +1636,8 @@ static struct cake_tin_data *cake_select_tin(struct Qdisc *sch,  		tin = q->tin_order[TC_H_MIN(skb->priority) - 1];  	else { +		if (!wash) +			dscp = cake_handle_diffserv(skb, wash);  		tin = q->tin_index[dscp];  		if (unlikely(tin >= q->tin_cnt)) @@ -2691,7 +2715,7 @@ static int cake_init(struct Qdisc *sch, struct nlattr *opt,  	qdisc_watchdog_init(&q->watchdog, sch);  	if (opt) { -		int err = cake_change(sch, opt, extack); +		err = cake_change(sch, opt, extack);  		if (err)  			return err; @@ -3008,7 +3032,7 @@ static int cake_dump_class_stats(struct Qdisc *sch, unsigned long cl,  			PUT_STAT_S32(BLUE_TIMER_US,  				     ktime_to_us(  					     ktime_sub(now, -						     flow->cvars.blue_timer))); +						       flow->cvars.blue_timer)));  		}  		if (flow->cvars.dropping) {  			PUT_STAT_S32(DROP_NEXT_US,  | 
