diff options
Diffstat (limited to 'net/sched/sch_choke.c')
| -rw-r--r-- | net/sched/sch_choke.c | 161 | 
1 files changed, 59 insertions, 102 deletions
diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index 3422b25df9e..e465064d39a 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c @@ -19,10 +19,7 @@  #include <net/pkt_sched.h>  #include <net/inet_ecn.h>  #include <net/red.h> -#include <linux/ip.h> -#include <net/ip.h> -#include <linux/ipv6.h> -#include <net/ipv6.h> +#include <net/flow_keys.h>  /*     CHOKe stateless AQM for fair bandwidth allocation @@ -60,6 +57,7 @@ struct choke_sched_data {  	struct red_parms parms;  /* Variables */ +	struct red_vars  vars;  	struct tcf_proto *filter_list;  	struct {  		u32	prob_drop;	/* Early probability drops */ @@ -142,85 +140,10 @@ static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx)  	--sch->q.qlen;  } -/* - * Compare flow of two packets - *  Returns true only if source and destination address and port match. - *          false for special cases - */ -static bool choke_match_flow(struct sk_buff *skb1, -			     struct sk_buff *skb2) -{ -	int off1, off2, poff; -	const u32 *ports1, *ports2; -	u8 ip_proto; -	__u32 hash1; - -	if (skb1->protocol != skb2->protocol) -		return false; - -	/* Use hash value as quick check -	 * Assumes that __skb_get_rxhash makes IP header and ports linear -	 */ -	hash1 = skb_get_rxhash(skb1); -	if (!hash1 || hash1 != skb_get_rxhash(skb2)) -		return false; - -	/* Probably match, but be sure to avoid hash collisions */ -	off1 = skb_network_offset(skb1); -	off2 = skb_network_offset(skb2); - -	switch (skb1->protocol) { -	case __constant_htons(ETH_P_IP): { -		const struct iphdr *ip1, *ip2; - -		ip1 = (const struct iphdr *) (skb1->data + off1); -		ip2 = (const struct iphdr *) (skb2->data + off2); - -		ip_proto = ip1->protocol; -		if (ip_proto != ip2->protocol || -		    ip1->saddr != ip2->saddr || ip1->daddr != ip2->daddr) -			return false; - -		if (ip_is_fragment(ip1) | ip_is_fragment(ip2)) -			ip_proto = 0; -		off1 += ip1->ihl * 4; -		off2 += ip2->ihl * 4; -		break; -	} - -	case __constant_htons(ETH_P_IPV6): { -		const struct ipv6hdr *ip1, *ip2; - -		ip1 = (const struct ipv6hdr *) (skb1->data + off1); -		ip2 = (const struct ipv6hdr *) (skb2->data + off2); - -		ip_proto = ip1->nexthdr; -		if (ip_proto != ip2->nexthdr || -		    ipv6_addr_cmp(&ip1->saddr, &ip2->saddr) || -		    ipv6_addr_cmp(&ip1->daddr, &ip2->daddr)) -			return false; -		off1 += 40; -		off2 += 40; -	} - -	default: /* Maybe compare MAC header here? */ -		return false; -	} - -	poff = proto_ports_offset(ip_proto); -	if (poff < 0) -		return true; - -	off1 += poff; -	off2 += poff; - -	ports1 = (__force u32 *)(skb1->data + off1); -	ports2 = (__force u32 *)(skb2->data + off2); -	return *ports1 == *ports2; -} -  struct choke_skb_cb { -	u16 classid; +	u16			classid; +	u8			keys_valid; +	struct flow_keys	keys;  };  static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb) @@ -241,6 +164,32 @@ static u16 choke_get_classid(const struct sk_buff *skb)  }  /* + * Compare flow of two packets + *  Returns true only if source and destination address and port match. + *          false for special cases + */ +static bool choke_match_flow(struct sk_buff *skb1, +			     struct sk_buff *skb2) +{ +	if (skb1->protocol != skb2->protocol) +		return false; + +	if (!choke_skb_cb(skb1)->keys_valid) { +		choke_skb_cb(skb1)->keys_valid = 1; +		skb_flow_dissect(skb1, &choke_skb_cb(skb1)->keys); +	} + +	if (!choke_skb_cb(skb2)->keys_valid) { +		choke_skb_cb(skb2)->keys_valid = 1; +		skb_flow_dissect(skb2, &choke_skb_cb(skb2)->keys); +	} + +	return !memcmp(&choke_skb_cb(skb1)->keys, +		       &choke_skb_cb(skb2)->keys, +		       sizeof(struct flow_keys)); +} + +/*   * Classify flow using either:   *  1. pre-existing classification result in skb   *  2. fast internal classification @@ -317,7 +266,7 @@ static bool choke_match_random(const struct choke_sched_data *q,  static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)  {  	struct choke_sched_data *q = qdisc_priv(sch); -	struct red_parms *p = &q->parms; +	const struct red_parms *p = &q->parms;  	int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;  	if (q->filter_list) { @@ -326,14 +275,15 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)  			goto other_drop;	/* Packet was eaten by filter */  	} +	choke_skb_cb(skb)->keys_valid = 0;  	/* Compute average queue usage (see RED) */ -	p->qavg = red_calc_qavg(p, sch->q.qlen); -	if (red_is_idling(p)) -		red_end_of_idle_period(p); +	q->vars.qavg = red_calc_qavg(p, &q->vars, sch->q.qlen); +	if (red_is_idling(&q->vars)) +		red_end_of_idle_period(&q->vars);  	/* Is queue small? */ -	if (p->qavg <= p->qth_min) -		p->qcount = -1; +	if (q->vars.qavg <= p->qth_min) +		q->vars.qcount = -1;  	else {  		unsigned int idx; @@ -345,8 +295,8 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)  		}  		/* Queue is large, always mark/drop */ -		if (p->qavg > p->qth_max) { -			p->qcount = -1; +		if (q->vars.qavg > p->qth_max) { +			q->vars.qcount = -1;  			sch->qstats.overlimits++;  			if (use_harddrop(q) || !use_ecn(q) || @@ -356,10 +306,10 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)  			}  			q->stats.forced_mark++; -		} else if (++p->qcount) { -			if (red_mark_probability(p, p->qavg)) { -				p->qcount = 0; -				p->qR = red_random(p); +		} else if (++q->vars.qcount) { +			if (red_mark_probability(p, &q->vars, q->vars.qavg)) { +				q->vars.qcount = 0; +				q->vars.qR = red_random(p);  				sch->qstats.overlimits++;  				if (!use_ecn(q) || !INET_ECN_set_ce(skb)) { @@ -370,7 +320,7 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)  				q->stats.prob_mark++;  			}  		} else -			p->qR = red_random(p); +			q->vars.qR = red_random(p);  	}  	/* Admit new packet */ @@ -404,8 +354,8 @@ static struct sk_buff *choke_dequeue(struct Qdisc *sch)  	struct sk_buff *skb;  	if (q->head == q->tail) { -		if (!red_is_idling(&q->parms)) -			red_start_of_idle_period(&q->parms); +		if (!red_is_idling(&q->vars)) +			red_start_of_idle_period(&q->vars);  		return NULL;  	} @@ -428,8 +378,8 @@ static unsigned int choke_drop(struct Qdisc *sch)  	if (len > 0)  		q->stats.other++;  	else { -		if (!red_is_idling(&q->parms)) -			red_start_of_idle_period(&q->parms); +		if (!red_is_idling(&q->vars)) +			red_start_of_idle_period(&q->vars);  	}  	return len; @@ -439,12 +389,13 @@ static void choke_reset(struct Qdisc *sch)  {  	struct choke_sched_data *q = qdisc_priv(sch); -	red_restart(&q->parms); +	red_restart(&q->vars);  }  static const struct nla_policy choke_policy[TCA_CHOKE_MAX + 1] = {  	[TCA_CHOKE_PARMS]	= { .len = sizeof(struct tc_red_qopt) },  	[TCA_CHOKE_STAB]	= { .len = RED_STAB_SIZE }, +	[TCA_CHOKE_MAX_P]	= { .type = NLA_U32 },  }; @@ -466,6 +417,7 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt)  	int err;  	struct sk_buff **old = NULL;  	unsigned int mask; +	u32 max_P;  	if (opt == NULL)  		return -EINVAL; @@ -478,6 +430,8 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt)  	    tb[TCA_CHOKE_STAB] == NULL)  		return -EINVAL; +	max_P = tb[TCA_CHOKE_MAX_P] ? nla_get_u32(tb[TCA_CHOKE_MAX_P]) : 0; +  	ctl = nla_data(tb[TCA_CHOKE_PARMS]);  	if (ctl->limit > CHOKE_MAX_QUEUE) @@ -527,10 +481,12 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt)  	red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog,  		      ctl->Plog, ctl->Scell_log, -		      nla_data(tb[TCA_CHOKE_STAB])); +		      nla_data(tb[TCA_CHOKE_STAB]), +		      max_P); +	red_set_vars(&q->vars);  	if (q->head == q->tail) -		red_end_of_idle_period(&q->parms); +		red_end_of_idle_period(&q->vars);  	sch_tree_unlock(sch);  	choke_free(old); @@ -561,6 +517,7 @@ static int choke_dump(struct Qdisc *sch, struct sk_buff *skb)  		goto nla_put_failure;  	NLA_PUT(skb, TCA_CHOKE_PARMS, sizeof(opt), &opt); +	NLA_PUT_U32(skb, TCA_CHOKE_MAX_P, q->parms.max_P);  	return nla_nest_end(skb, opts);  nla_put_failure:  |