From: Stephen Hemminger <hidden> Date: 2011-01-05 00:29:33
This implements the CHOKe packet scheduler based on the existing
Linux RED scheduler based on the algorithm described in the paper.
Configuration is the same as RED; only the name changes.
The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth) {
Queue the new packet
}
Else {
Select randomly a packet from the queue for their flow id
Compare arriving packet with a randomly selected packet.
If they have the same flow id {
Drop both the packets
}
Else {
if (Qave ≥ maxth) {
Calculate the dropping probability pa
Drop the packet with probability pa
}
Else {
Drop the new packet
}
}
}
This an early access version.
Signed-off-by: Stephen Hemminger <redacted>
---
net/sched/Kconfig | 11 +
net/sched/Makefile | 1
net/sched/sch_choke.c | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 376 insertions(+)
From: Eric Dumazet <hidden> Date: 2011-01-05 06:02:30
Le mardi 04 janvier 2011 à 16:29 -0800, Stephen Hemminger a écrit :
This implements the CHOKe packet scheduler based on the existing
Linux RED scheduler based on the algorithm described in the paper.
Configuration is the same as RED; only the name changes.
The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth) {
Queue the new packet
}
Else {
Select randomly a packet from the queue for their flow id
Compare arriving packet with a randomly selected packet.
If they have the same flow id {
Drop both the packets
}
Else {
if (Qave ≥ maxth) {
you mean if (Qave is less than maxth) ?
Calculate the dropping probability pa
Drop the packet with probability pa
}
Else {
Drop the new packet
}
}
}
This an early access version.
Big packets are really unfair. Must disable TSO/GRO :)
quoted hunk
++ */++struct choke_sched_data+{+ u32 limit;+ unsigned char flags;++ struct red_parms parms;+ struct red_stats stats;+};++/* Select a packet at random from the list.+ * Same caveats as skb_peek.+ */+static struct sk_buff *skb_peek_random(struct sk_buff_head *list)+{+ struct sk_buff *skb = list->next;+ unsigned int idx = net_random() % list->qlen;++ while (skb && idx-- > 0)+ skb = skb->next;
Ouch... A linked list (using in skb anchors) is not appropriate data
structure. Thats too many cache lines misses.
Maybe using a q->limit array of skb pointers ?
quoted hunk
++ return skb;+}++/* Given IP header and size find src/dst port pair */+static inline u32 get_ports(const void *hdr, size_t hdr_size, int offset)+{+ return *(u32 *)(hdr + hdr_size + offset);+}+++static bool same_flow(struct sk_buff *nskb, const struct sk_buff *oskb)+{+ if (nskb->protocol != oskb->protocol)+ return false;++ switch (nskb->protocol) {+ case htons(ETH_P_IP):+ {+ const struct iphdr *iph1, *iph2;+ int poff;++ if (!pskb_network_may_pull(nskb, sizeof(*iph1)))+ return false;
Why isnt it necessary to also may_pull test oskb ?
quoted hunk
++ iph1 = ip_hdr(nskb);+ iph2 = ip_hdr(oskb);++ if (iph1->protocol != iph2->protocol ||+ iph1->daddr != iph2->daddr ||+ iph1->saddr != iph2->saddr)+ return false;++ /* Be hostile to new fragmented packets */+ if (iph1->frag_off & htons(IP_MF|IP_OFFSET))+ return true;++ if (iph2->frag_off & htons(IP_MF|IP_OFFSET))+ return false;++ poff = proto_ports_offset(iph1->protocol);+ if (poff >= 0 &&+ pskb_network_may_pull(nskb, iph1->ihl * 4 + 4 + poff)) {+ iph1 = ip_hdr(nskb);++ return get_ports(iph1, iph1->ihl * 4, poff)+ == get_ports(iph2, iph2->ihl * 4, poff);+ }++ return false;+ }++ case htons(ETH_P_IPV6):+ {+ const struct ipv6hdr *iph1, *iph2;+ int poff;++ if (!pskb_network_may_pull(nskb, sizeof(*iph1)))+ return false;
same here.
quoted hunk
++ iph1 = ipv6_hdr(nskb);+ iph2 = ipv6_hdr(oskb);++ if (iph1->nexthdr != iph2->nexthdr ||+ ipv6_addr_cmp(&iph1->daddr, &iph2->daddr) != 0 ||+ ipv6_addr_cmp(&iph1->saddr, &iph2->saddr) != 0)+ return false;++ poff = proto_ports_offset(iph1->nexthdr);+ if (poff >= 0 &&+ pskb_network_may_pull(nskb, sizeof(*iph1) + 4 + poff)) {+ iph1 = ipv6_hdr(nskb);++ return get_ports(iph1, sizeof(*iph1), poff)+ == get_ports(iph2, sizeof(*iph2), poff);+ }+ return false;+ }+ default:+ return false;+ }++}++/*+ * Decide what to do with new packet based on queue size.+ * returns 1 if packet should be admitted+ * 0 if packet should be dropped+ */+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;++ p->qavg = red_calc_qavg(p, skb_queue_len(&sch->q));+ if (red_is_idling(p))+ red_end_of_idle_period(p);++ if (p->qavg <= p->qth_min)+ p->qcount = -1;+ else {+ struct sk_buff *oskb;++ /* Draw a packet at random from queue */+ oskb = skb_peek_random(&sch->q);++ /* Both packets from same flow? */+ if (same_flow(skb, oskb)) {+ /* Drop both packets */+ __skb_unlink(oskb, &sch->q);+ qdisc_drop(oskb, sch);+ goto congestion_drop;+ }++ if (p->qavg > p->qth_max) {
ok, maybe CHOKe paper used : if (p->qavg >= p->qth_max) ?
You could avoid the divide op :
unsigned int idx = reciprocal_divide(random32(), list->qlen);
How would this work, it is a mod not a divide??
It works, because random32() provides a 32bit 'random' number.
between 0 and 0xFFFFFFFF
We multiply it by X, get a 64bit number between 0 and 0xFFFFFFFF * X
then we right shift it by 32, get a number between 0 and X - 1
We dont need to get the modulus, just a random number between 0 and X -
1
Dont worry, we should add a helper function to do that, since it might
be used in many places.
/* deliver a random number between 0 and N - 1 */
u32 random_N(unsigned int N)
{
reciprocal_divide(random32(), N);
}
From: Stephen Hemminger <hidden> Date: 2011-01-05 19:21:08
This implements the CHOKe packet scheduler based on the existing
Linux RED scheduler based on the algorithm described in the paper.
The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue
if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with probability p (same as RED)
See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.
Signed-off-by: Stephen Hemminger <redacted>
---
New in this version:
- use skb hash values for flow matching
- reciprocal_divide optimization
- optional ECN support (same as RED)
net/sched/Kconfig | 11 +
net/sched/Makefile | 1
net/sched/sch_choke.c | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 376 insertions(+)
From: Eric Dumazet <hidden> Date: 2011-01-05 20:06:12
Le mercredi 05 janvier 2011 à 11:21 -0800, Stephen Hemminger a écrit :
This implements the CHOKe packet scheduler based on the existing
Linux RED scheduler based on the algorithm described in the paper.
quoted hunk
++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;++ p->qavg = red_calc_qavg(p, skb_queue_len(&sch->q));+ if (red_is_idling(p))+ red_end_of_idle_period(p);++ if (p->qavg <= p->qth_min)+ p->qcount = -1;+ else {+ struct sk_buff *oskb;++ /* Draw a packet at random from queue */+ oskb = skb_peek_random(&sch->q);++ /* Both packets from same flow?+ * Assumes skb_get_rxhash already set hash on oskb->rxhash+ * prior to queuing
but this is not true... if at that time, p->qavg was <= p->qth_min.
Packet was directly enqueued.
Just use :
if (skb_get_rxhash(oskb) == skb_get_rxhash(skb))
Since skb_get_rxhash(skb) doesnt recompute rxhash if already set.
Hmm... I am now wondering if this actually works on egress at all
(can we use rxhash here I mean)
quoted hunk
+ */+ if (oskb->rxhash == skb_get_rxhash(skb)) {+ /* Drop both packets */+ __skb_unlink(oskb, &sch->q);+ qdisc_drop(oskb, sch);+ goto congestion_drop;+ }
From: Stephen Hemminger <hidden> Date: 2011-01-05 20:15:56
On Wed, 05 Jan 2011 21:06:07 +0100
Eric Dumazet [off-list ref] wrote:
Le mercredi 05 janvier 2011 à 11:21 -0800, Stephen Hemminger a écrit :
quoted
This implements the CHOKe packet scheduler based on the existing
Linux RED scheduler based on the algorithm described in the paper.
quoted
++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;++ p->qavg = red_calc_qavg(p, skb_queue_len(&sch->q));+ if (red_is_idling(p))+ red_end_of_idle_period(p);++ if (p->qavg <= p->qth_min)+ p->qcount = -1;+ else {+ struct sk_buff *oskb;++ /* Draw a packet at random from queue */+ oskb = skb_peek_random(&sch->q);++ /* Both packets from same flow?+ * Assumes skb_get_rxhash already set hash on oskb->rxhash+ * prior to queuing
but this is not true... if at that time, p->qavg was <= p->qth_min.
Packet was directly enqueued.
Just use :
if (skb_get_rxhash(oskb) == skb_get_rxhash(skb))
Since skb_get_rxhash(skb) doesnt recompute rxhash if already set.
Hmm... I am now wondering if this actually works on egress at all
(can we use rxhash here I mean)
quoted
+ */+ if (oskb->rxhash == skb_get_rxhash(skb)) {+ /* Drop both packets */+ __skb_unlink(oskb, &sch->q);+ qdisc_drop(oskb, sch);+ goto congestion_drop;+ }
The code computes a value, whether it is correct or not is another question.
Also a little concerned that different NIC's compute different flow hash
values which could cause false positives.
--
From: Eric Dumazet <hidden> Date: 2011-01-06 04:07:36
Le mercredi 05 janvier 2011 à 11:21 -0800, Stephen Hemminger a écrit :
This implements the CHOKe packet scheduler based on the existing
Linux RED scheduler based on the algorithm described in the paper.
The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue
if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with probability p (same as RED)
See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.
Signed-off-by: Stephen Hemminger <redacted>
To be really useful in a wide range of environments, I believe that :
- CHOKe should be able to use an external flow classifier (like say...
SFQ) to compute a token and compare two skbs by this token instead of
custom rxhash or whatever. (rxhash can be the default in absence of flow
classifier). Probably you need to store the token in skb->cb[] to avoid
calling tc_classify() several times for a given packet.
http://lwn.net/Articles/236200/http://kerneltrap.org/mailarchive/linux-netdev/2008/1/31/667679
- Must use a FIFO with O(1) access to Nth skb in queue.
A linked list makes this implementation too expensive for big queues.
For small queues (less than 128 skbs at this moment for SFQ), existing
schedulers are good enough.
CHOKe authors dont mention this in their paper, but their experiments
were done in 1999 with 1Mbs links. minth=100 and maxth=200, limit=300
We want to try CHOKe with modern links, probably with minth=2000 and
maxth=4000 or more.
They said "It is arguably more difficult to drop a randomy chosen packet
since this means removing from a linked-list. Instead of doing this, we
propose to add one extra bit to the packet header. The bit is set to one
if the drop candidate is to be dropped. When a packet advance to the
head of the FIFO buffer, the status of the bit determines whether it is
to be immediately discarded or transmitted on the outgoind line"
If they thought removing a buffer from a linked list was expensive
(!!!), they certainly assumed the previous access to the randomly chosen
buffer was faster than the skb unlink !
Using a circular buffer should be enough, using a similar trick than
suggested : when droping an skb from the ring, stick a NULL pointer and
dont memmove() the window to shrink it.
struct skb_ring {
unsigned int head;
unsigned int tail;
unsigned int size; /* a power of two */
struct sk_buff **table;
};
Doing so avoids the cache misses to adjacent skbs prev/next when
queue/dequeue is done.
From: Stephen Hemminger <hidden> Date: 2011-01-06 06:53:12
On Thu, 06 Jan 2011 05:07:30 +0100
Eric Dumazet [off-list ref] wrote:
- CHOKe should be able to use an external flow classifier (like say...
SFQ) to compute a token and compare two skbs by this token instead of
custom rxhash or whatever.
I don't think you can apply a filter to a non-classful qdisc?
From: Stephen Hemminger <hidden> Date: 2011-01-07 04:55:57
On Thu, 06 Jan 2011 05:07:30 +0100
Eric Dumazet [off-list ref] wrote:
Le mercredi 05 janvier 2011 à 11:21 -0800, Stephen Hemminger a écrit :
quoted
This implements the CHOKe packet scheduler based on the existing
Linux RED scheduler based on the algorithm described in the paper.
The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue
if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with probability p (same as RED)
See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.
Signed-off-by: Stephen Hemminger <redacted>
To be really useful in a wide range of environments, I believe that :
- CHOKe should be able to use an external flow classifier (like say...
SFQ) to compute a token and compare two skbs by this token instead of
custom rxhash or whatever. (rxhash can be the default in absence of flow
classifier). Probably you need to store the token in skb->cb[] to avoid
calling tc_classify() several times for a given packet.
http://lwn.net/Articles/236200/http://kerneltrap.org/mailarchive/linux-netdev/2008/1/31/667679
Probably should split SFQ flow hash stuff into core code for reuse.
- Must use a FIFO with O(1) access to Nth skb in queue.
A linked list makes this implementation too expensive for big queues.
For small queues (less than 128 skbs at this moment for SFQ), existing
schedulers are good enough.
CHOKe authors dont mention this in their paper, but their experiments
were done in 1999 with 1Mbs links. minth=100 and maxth=200, limit=300
We want to try CHOKe with modern links, probably with minth=2000 and
maxth=4000 or more.
They said "It is arguably more difficult to drop a randomy chosen packet
since this means removing from a linked-list. Instead of doing this, we
propose to add one extra bit to the packet header. The bit is set to one
if the drop candidate is to be dropped. When a packet advance to the
head of the FIFO buffer, the status of the bit determines whether it is
to be immediately discarded or transmitted on the outgoind line"
If they thought removing a buffer from a linked list was expensive
(!!!), they certainly assumed the previous access to the randomly chosen
buffer was faster than the skb unlink !
Using a circular buffer should be enough, using a similar trick than
suggested : when droping an skb from the ring, stick a NULL pointer and
dont memmove() the window to shrink it.
struct skb_ring {
unsigned int head;
unsigned int tail;
unsigned int size; /* a power of two */
struct sk_buff **table;
};
Doing so avoids the cache misses to adjacent skbs prev/next when
queue/dequeue is done.
The problem is that large tables of pointers in kernel require either
contiguous allocation or some indirect table algorithm.
--
On Fri, Jan 7, 2011 at 12:55 PM, Stephen Hemminger
[off-list ref] wrote:
On Thu, 06 Jan 2011 05:07:30 +0100
Eric Dumazet [off-list ref] wrote:
quoted
Le mercredi 05 janvier 2011 à 11:21 -0800, Stephen Hemminger a écrit :
quoted
This implements the CHOKe packet scheduler based on the existing
Linux RED scheduler based on the algorithm described in the paper.
The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue
if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with probability p (same as RED)
See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.
Signed-off-by: Stephen Hemminger <redacted>
To be really useful in a wide range of environments, I believe that :
- CHOKe should be able to use an external flow classifier (like say...
SFQ) to compute a token and compare two skbs by this token instead of
custom rxhash or whatever. (rxhash can be the default in absence of flow
classifier). Probably you need to store the token in skb->cb[] to avoid
calling tc_classify() several times for a given packet.
http://lwn.net/Articles/236200/http://kerneltrap.org/mailarchive/linux-netdev/2008/1/31/667679
Probably should split SFQ flow hash stuff into core code for reuse.
We need not do that, since we have sch_drr and cls_flow. :)
--
Regards,
Changli Gao(xiaosuo@gmail.com)
From: Stephen Hemminger <hidden> Date: 2011-01-07 07:10:46
On Fri, 7 Jan 2011 13:39:26 +0800
Changli Gao [off-list ref] wrote:
On Fri, Jan 7, 2011 at 12:55 PM, Stephen Hemminger
[off-list ref] wrote:
quoted
On Thu, 06 Jan 2011 05:07:30 +0100
Eric Dumazet [off-list ref] wrote:
quoted
Le mercredi 05 janvier 2011 à 11:21 -0800, Stephen Hemminger a écrit :
quoted
This implements the CHOKe packet scheduler based on the existing
Linux RED scheduler based on the algorithm described in the paper.
The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue
if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with probability p (same as RED)
See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.
Signed-off-by: Stephen Hemminger <redacted>
To be really useful in a wide range of environments, I believe that :
- CHOKe should be able to use an external flow classifier (like say...
SFQ) to compute a token and compare two skbs by this token instead of
custom rxhash or whatever. (rxhash can be the default in absence of flow
classifier). Probably you need to store the token in skb->cb[] to avoid
calling tc_classify() several times for a given packet.
http://lwn.net/Articles/236200/http://kerneltrap.org/mailarchive/linux-netdev/2008/1/31/667679
Probably should split SFQ flow hash stuff into core code for reuse.
We need not do that, since we have sch_drr and cls_flow. :)
I prefer that the qdisc be useable without any explicit flow classification.
I.e like SFQ it should fall back to a sensible flow matching. DRR and others
put everything in one flow, if no filters are used.
From: Eric Dumazet <hidden> Date: 2011-01-07 08:37:28
Le jeudi 06 janvier 2011 à 20:55 -0800, Stephen Hemminger a écrit :
The problem is that large tables of pointers in kernel require either
contiguous allocation or some indirect table algorithm.
By large table, how many slots do you envision ?
1024 ?
8192 ?
65536 ?
Even insane value like 65536 is OK most of the time.
if kmalloc(GFP_KERNEL) fails, try vmalloc().
We are in process context and are allowed to sleep when qdisc is
created.
Anyway, accessing a random skb in a list of 65536 skbs is just crazy.
@@ -0,0 +1,388 @@+/*+*net/sched/sch_choke.cCHOKEscheduler+*+*Copyright(c)2011StephenHemminger<shemminger@vyatta.com>+*Copyright(c)2011EricDumazet<eric.dumazet@gmail.com>+*+*Thisprogramisfreesoftware;youcanredistributeitand/or+*modifyitunderthetermsoftheGNUGeneralPublicLicense+*version2aspublishedbytheFreeSoftwareFoundation.+*+*/++#include<linux/module.h>+#include<linux/types.h>+#include<linux/kernel.h>+#include<linux/skbuff.h>+#include<linux/reciprocal_div.h>+#include<net/pkt_sched.h>+#include<net/inet_ecn.h>+#include<net/red.h>++/* CHOKe stateless AQM for fair bandwidth allocation+=================================================++CHOKe(CHOoseandKeepforresponsiveflows,CHOoseandKillfor+unresponsiveflows)isavariantofREDthatpenalizesmisbehavingflowsbut+maintainsnoflowstate.ThedifferencefromREDisanadditionalstep+duringtheenqueuingprocess.Ifaveragequeuesizeisoverthe+lowthreshold(qmin),apacketischosenatrandomfromthequeue.+Ifboththenewandchosenpacketarefromthesameflow,both+aredropped.UnlikeRED,CHOKeisnota"classful"qdiscbecauseit+needstoaccesspacketsinqueuerandomly.++Source:+R.Pan,B.Prabhakar,andK.Psounis,"CHOKe, A Stateless+ActiveQueueManagementSchemeforApproximatingFairBandwidthAllocation",+IEEEINFOCOM,2000.++A.Tang,J.Wang,S.Low,"Understanding CHOKe: Throughput and Spatial+Characteristics", IEEE/ACM Transactions on Networking, 2004++*/++structchoke_sched_data{+u32limit;+unsignedcharflags;++structred_parmsparms;+structred_statsstats;++unsignedinthead;+unsignedinttail;+unsignedintholes;+unsignedinttab_mask;/* size - 1 */+structsk_buff**tab;+};++staticinlineunsignedintchoke_len(conststructchoke_sched_data*q)+{+return(q->tail-q->head)&q->tab_mask;+}++/* deliver a random number between 0 and N - 1 */+staticinlineu32random_N(unsignedintN)+{+returnreciprocal_divide(random32(),N);+}++/* Select a packet at random from the queue in O(1) */+staticstructsk_buff*choke_peek_random(structchoke_sched_data*q,unsignedint*pidx)+{+*pidx=(q->head+random_N(choke_len(q)))&q->tab_mask;+returnq->tab[*pidx];+}+++staticinlineintuse_ecn(conststructchoke_sched_data*q)+{+returnq->flags&TC_RED_ECN;+}++staticinlineintuse_harddrop(conststructchoke_sched_data*q)+{+returnq->flags&TC_RED_HARDDROP;+}++staticinlinevoidchoke_zap_head_holes(structchoke_sched_data*q)+{+while(q->holes&&q->tab[q->head]==NULL){+q->head=(q->head+1)&q->tab_mask;+q->holes--;+}+}++staticinlinevoidchoke_zap_tail_holes(structchoke_sched_data*q)+{+while(q->holes&&q->tab[q->tail-1]==NULL){+q->tail=(q->tail-1)&q->tab_mask;+q->holes--;+}+}++staticvoidchoke_drop_by_idx(structchoke_sched_data*q,unsignedintidx)+{+q->tab[idx]=NULL;+q->holes++;+choke_zap_head_holes(q);+choke_zap_tail_holes(q);+}+++staticintchoke_enqueue(structsk_buff*skb,structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);+structred_parms*p=&q->parms;++p->qavg=red_calc_qavg(p,choke_len(q)-q->holes);+if(red_is_idling(p))+red_end_of_idle_period(p);++if(p->qavg<=p->qth_min)+p->qcount=-1;+else{+structsk_buff*oskb;+unsignedintidx;++/* Draw a packet at random from queue */+oskb=choke_peek_random(q,&idx);++/* Both packets from same flow ? */+if(oskb&&skb_get_rxhash(oskb)==skb_get_rxhash(skb)){+/* Drop both packets */+choke_drop_by_idx(q,idx);+qdisc_drop(oskb,sch);+gotocongestion_drop;+}++if(p->qavg>p->qth_max){+p->qcount=-1;++sch->qstats.overlimits++;+if(use_harddrop(q)||!use_ecn(q)||+!INET_ECN_set_ce(skb)){+q->stats.forced_drop++;+gotocongestion_drop;+}++q->stats.forced_mark++;+}++if(++p->qcount){+if(red_mark_probability(p,p->qavg)){+p->qcount=0;+p->qR=red_random(p);++sch->qstats.overlimits++;+if(!use_ecn(q)||!INET_ECN_set_ce(skb)){+q->stats.prob_drop++;+gotocongestion_drop;+}++q->stats.prob_mark++;+}+}else+p->qR=red_random(p);+}++/* Admit new packet */+if(likely(choke_len(q)<q->limit)){+q->tab[q->tail]=skb;+q->tail=(q->tail+1)&q->tab_mask;+sch->qstats.backlog+=qdisc_pkt_len(skb);+__qdisc_update_bstats(sch,qdisc_pkt_len(skb));+returnNET_XMIT_SUCCESS;+}+q->stats.pdrop++;+sch->qstats.drops++;+kfree_skb(skb);+returnNET_XMIT_DROP;++congestion_drop:+qdisc_drop(skb,sch);+returnNET_XMIT_CN;+}++staticstructsk_buff*choke_dequeue(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);+structsk_buff*skb;++if(q->head==q->tail){+if(!red_is_idling(&q->parms))+red_start_of_idle_period(&q->parms);+returnNULL;+}+skb=q->tab[q->head];+q->tab[q->head]=NULL;/* not really needed */+q->head=(q->head+1)&q->tab_mask;+choke_zap_head_holes(q);+sch->qstats.backlog-=qdisc_pkt_len(skb);++returnskb;+}++staticunsignedintchoke_drop(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);+unsignedintlen;++len=qdisc_queue_drop(sch);++if(len>0)+q->stats.other++;+else{+if(!red_is_idling(&q->parms))+red_start_of_idle_period(&q->parms);+}++returnlen;+}++staticvoidchoke_reset(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);++red_restart(&q->parms);+}++staticconststructnla_policychoke_policy[TCA_RED_MAX+1]={+[TCA_RED_PARMS]={.len=sizeof(structtc_red_qopt)},+[TCA_RED_STAB]={.len=RED_STAB_SIZE},+};+++staticvoidchoke_free(void*addr)+{+if(addr){+if(is_vmalloc_addr(addr))+vfree(addr);+else+kfree(addr);+}+}++staticintchoke_change(structQdisc*sch,structnlattr*opt)+{+structchoke_sched_data*q=qdisc_priv(sch);+structnlattr*tb[TCA_RED_MAX+1];+structtc_red_qopt*ctl;+interr;+structsk_buff**old=NULL;+unsignedintmask;++if(opt==NULL)+return-EINVAL;++err=nla_parse_nested(tb,TCA_RED_MAX,opt,choke_policy);+if(err<0)+returnerr;++if(tb[TCA_RED_PARMS]==NULL||+tb[TCA_RED_STAB]==NULL)+return-EINVAL;++ctl=nla_data(tb[TCA_RED_PARMS]);++mask=roundup_pow_of_two(ctl->limit+1)-1;+if(mask!=q->tab_mask){+structsk_buff**ntab=kcalloc(mask+1,sizeof(structsk_buff*),+GFP_KERNEL);+if(!ntab)+ntab=vzalloc((mask+1)*sizeof(structsk_buff*));+if(!ntab)+return-ENOMEM;+sch_tree_lock(sch);+old=q->tab;+if(old){+unsignedinttail=0;++while(q->head!=q->tail){+ntab[tail++]=q->tab[q->head];+q->head=(q->head+1)&q->tab_mask;+}+q->head=0;+q->tail=tail;+}+q->tab_mask=mask;+q->holes=0;+}else+sch_tree_lock(sch);+q->flags=ctl->flags;+q->limit=ctl->limit;++red_set_parms(&q->parms,ctl->qth_min,ctl->qth_max,ctl->Wlog,+ctl->Plog,ctl->Scell_log,+nla_data(tb[TCA_RED_STAB]));++if(q->head==q->tail)+red_end_of_idle_period(&q->parms);++sch_tree_unlock(sch);+choke_free(old);+return0;+}++staticintchoke_init(structQdisc*sch,structnlattr*opt)+{+returnchoke_change(sch,opt);+}++staticintchoke_dump(structQdisc*sch,structsk_buff*skb)+{+structchoke_sched_data*q=qdisc_priv(sch);+structnlattr*opts=NULL;+structtc_red_qoptopt={+.limit=q->limit,+.flags=q->flags,+.qth_min=q->parms.qth_min>>q->parms.Wlog,+.qth_max=q->parms.qth_max>>q->parms.Wlog,+.Wlog=q->parms.Wlog,+.Plog=q->parms.Plog,+.Scell_log=q->parms.Scell_log,+};++sch->q.qlen=choke_len(q)-q->holes;+opts=nla_nest_start(skb,TCA_OPTIONS);+if(opts==NULL)+gotonla_put_failure;++NLA_PUT(skb,TCA_RED_PARMS,sizeof(opt),&opt);+returnnla_nest_end(skb,opts);++nla_put_failure:+nla_nest_cancel(skb,opts);+return-EMSGSIZE;+}++staticintchoke_dump_stats(structQdisc*sch,structgnet_dump*d)+{+structchoke_sched_data*q=qdisc_priv(sch);+structtc_red_xstatsst={+.early=q->stats.prob_drop+q->stats.forced_drop,+.pdrop=q->stats.pdrop,+.other=q->stats.other,+.marked=q->stats.prob_mark+q->stats.forced_mark,+};++returngnet_stats_copy_app(d,&st,sizeof(st));+}++staticvoidchoke_destroy(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);++choke_free(q->tab);+}++staticstructQdisc_opschoke_qdisc_ops__read_mostly={+.id="choke",+.priv_size=sizeof(structchoke_sched_data),++.enqueue=choke_enqueue,+.dequeue=choke_dequeue,+.peek=qdisc_peek_head,+.drop=choke_drop,+.init=choke_init,+.destroy=choke_destroy,+.reset=choke_reset,+.change=choke_change,+.dump=choke_dump,+.dump_stats=choke_dump_stats,+.owner=THIS_MODULE,+};++staticint__initchoke_module_init(void)+{+returnregister_qdisc(&choke_qdisc_ops);+}++staticvoid__exitchoke_module_exit(void)+{+unregister_qdisc(&choke_qdisc_ops);+}++module_init(choke_module_init)+module_exit(choke_module_exit)++MODULE_LICENSE("GPL");
@@ -0,0 +1,388 @@+/*+*net/sched/sch_choke.cCHOKEscheduler+*+*Copyright(c)2011StephenHemminger<shemminger@vyatta.com>+*Copyright(c)2011EricDumazet<eric.dumazet@gmail.com>+*+*Thisprogramisfreesoftware;youcanredistributeitand/or+*modifyitunderthetermsoftheGNUGeneralPublicLicense+*version2aspublishedbytheFreeSoftwareFoundation.+*+*/++#include<linux/module.h>+#include<linux/types.h>+#include<linux/kernel.h>+#include<linux/skbuff.h>+#include<linux/reciprocal_div.h>+#include<net/pkt_sched.h>+#include<net/inet_ecn.h>+#include<net/red.h>++/* CHOKe stateless AQM for fair bandwidth allocation+=================================================++CHOKe(CHOoseandKeepforresponsiveflows,CHOoseandKillfor+unresponsiveflows)isavariantofREDthatpenalizesmisbehavingflowsbut+maintainsnoflowstate.ThedifferencefromREDisanadditionalstep+duringtheenqueuingprocess.Ifaveragequeuesizeisoverthe+lowthreshold(qmin),apacketischosenatrandomfromthequeue.+Ifboththenewandchosenpacketarefromthesameflow,both+aredropped.UnlikeRED,CHOKeisnota"classful"qdiscbecauseit+needstoaccesspacketsinqueuerandomly.++Source:+R.Pan,B.Prabhakar,andK.Psounis,"CHOKe, A Stateless+ActiveQueueManagementSchemeforApproximatingFairBandwidthAllocation",+IEEEINFOCOM,2000.++A.Tang,J.Wang,S.Low,"Understanding CHOKe: Throughput and Spatial+Characteristics", IEEE/ACM Transactions on Networking, 2004++*/++structchoke_sched_data{+u32limit;+unsignedcharflags;++structred_parmsparms;+structred_statsstats;++unsignedinthead;+unsignedinttail;+unsignedintholes;+unsignedinttab_mask;/* size - 1 */+structsk_buff**tab;+};++staticinlineunsignedintchoke_len(conststructchoke_sched_data*q)+{+return(q->tail-q->head)&q->tab_mask;+}++/* deliver a random number between 0 and N - 1 */+staticinlineu32random_N(unsignedintN)+{+returnreciprocal_divide(random32(),N);+}++/* Select a packet at random from the queue in O(1) */+staticstructsk_buff*choke_peek_random(structchoke_sched_data*q,unsignedint*pidx)+{+*pidx=(q->head+random_N(choke_len(q)))&q->tab_mask;+returnq->tab[*pidx];+}
I don't think this works right. The choke_peek_random could find a hole.
Either the data structure has to change, or the peek_random has to retry,
or if quick peek fails then compress the slot with memmove and retry.
--
@@ -0,0 +1,388 @@+/*+*net/sched/sch_choke.cCHOKEscheduler+*+*Copyright(c)2011StephenHemminger<shemminger@vyatta.com>+*Copyright(c)2011EricDumazet<eric.dumazet@gmail.com>+*+*Thisprogramisfreesoftware;youcanredistributeitand/or+*modifyitunderthetermsoftheGNUGeneralPublicLicense+*version2aspublishedbytheFreeSoftwareFoundation.+*+*/++#include<linux/module.h>+#include<linux/types.h>+#include<linux/kernel.h>+#include<linux/skbuff.h>+#include<linux/reciprocal_div.h>+#include<net/pkt_sched.h>+#include<net/inet_ecn.h>+#include<net/red.h>++/* CHOKe stateless AQM for fair bandwidth allocation+=================================================++CHOKe(CHOoseandKeepforresponsiveflows,CHOoseandKillfor+unresponsiveflows)isavariantofREDthatpenalizesmisbehavingflowsbut+maintainsnoflowstate.ThedifferencefromREDisanadditionalstep+duringtheenqueuingprocess.Ifaveragequeuesizeisoverthe+lowthreshold(qmin),apacketischosenatrandomfromthequeue.+Ifboththenewandchosenpacketarefromthesameflow,both+aredropped.UnlikeRED,CHOKeisnota"classful"qdiscbecauseit+needstoaccesspacketsinqueuerandomly.++Source:+R.Pan,B.Prabhakar,andK.Psounis,"CHOKe, A Stateless+ActiveQueueManagementSchemeforApproximatingFairBandwidthAllocation",+IEEEINFOCOM,2000.++A.Tang,J.Wang,S.Low,"Understanding CHOKe: Throughput and Spatial+Characteristics", IEEE/ACM Transactions on Networking, 2004++*/++structchoke_sched_data{+u32limit;+unsignedcharflags;++structred_parmsparms;+structred_statsstats;++unsignedinthead;+unsignedinttail;+unsignedintholes;+unsignedinttab_mask;/* size - 1 */+structsk_buff**tab;+};++staticinlineunsignedintchoke_len(conststructchoke_sched_data*q)+{+return(q->tail-q->head)&q->tab_mask;+}++/* deliver a random number between 0 and N - 1 */+staticinlineu32random_N(unsignedintN)+{+returnreciprocal_divide(random32(),N);+}++/* Select a packet at random from the queue in O(1) */+staticstructsk_buff*choke_peek_random(structchoke_sched_data*q,unsignedint*pidx)+{+*pidx=(q->head+random_N(choke_len(q)))&q->tab_mask;+returnq->tab[*pidx];+}
I don't think this works right. The choke_peek_random could find a hole.
Either the data structure has to change, or the peek_random has to retry,
or if quick peek fails then compress the slot with memmove and retry.
Yes, this "compress only if peek_random() finds a hole seems good, I'll
try this.
As number of holes is known, we could have :
if (holes_proportion_is_less_than_20_percent())
try another random number X times
else
compress table to remove holes.
From: Stephen Hemminger <hidden> Date: 2011-01-10 23:44:18
This implements the CHOKe packet scheduler based on the existing
Linux RED scheduler based on the algorithm described in the paper.
The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue
if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with proability p (same as RED)
See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.
Signed-off-by: Stephen Hemminger <redacted>
---
0.3 version from Eric uses table for queue.
0.4 allows classification with TC filters
fixes crash when peek_random() finds a hole
net/sched/Kconfig | 11 +
net/sched/Makefile | 1
net/sched/sch_choke.c | 527 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 539 insertions(+)
From: Eric Dumazet <hidden> Date: 2011-01-11 00:00:35
Le lundi 10 janvier 2011 à 15:44 -0800, Stephen Hemminger a écrit :
This implements the CHOKe packet scheduler based on the existing
Linux RED scheduler based on the algorithm described in the paper.
The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue
if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with proability p (same as RED)
See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.
Signed-off-by: Stephen Hemminger <redacted>
From: Eric Dumazet <hidden> Date: 2011-01-11 06:34:15
Le mardi 11 janvier 2011 à 07:18 +0100, Eric Dumazet a écrit :
Le lundi 10 janvier 2011 à 17:10 -0800, Stephen Hemminger a écrit :
quoted
OK, put that in.
Thanks !
quoted
quoted
Here we missed :
q->tab = ntab;
We also need to change
.peek = qdisc_peek_head,
to
.peek = choke_peek_head,
static struct sk_buff *choke_peek_head(struct Qdisc *sch)
{
struct choke_sched_data *q = qdisc_priv(sch);
return (q->head != q->tail) ? q->tab[q->head] : NULL;
}
And to correctly work with CBQ (at least...), we need to update
sch->q.qlen = choke_len(q) - q->holes;
in dequeue() and enqueue()
Here is the version I successfully tested, with 30000 packets in
queue :)
qdisc choke 11: parent 1:11 limit 70000b min 10000b max 30000b ewma 1 Plog 16 Scell_log 11
Sent 62099201 bytes 112920 pkt (dropped 367712, overlimits 282668 requeues 0)
rate 21344Kbit 4851pps backlog 39877589b 30001p requeues 0
marked 0 early 282668 pdrop 0 other 0
@@ -0,0 +1,536 @@+/*+*net/sched/sch_choke.cCHOKEscheduler+*+*Copyright(c)2011StephenHemminger<shemminger@vyatta.com>+*Copyright(c)2011EricDumazet<eric.dumazet@gmail.com>+*+*Thisprogramisfreesoftware;youcanredistributeitand/or+*modifyitunderthetermsoftheGNUGeneralPublicLicense+*version2aspublishedbytheFreeSoftwareFoundation.+*+*/++#include<linux/module.h>+#include<linux/types.h>+#include<linux/kernel.h>+#include<linux/skbuff.h>+#include<linux/reciprocal_div.h>+#include<net/pkt_sched.h>+#include<net/inet_ecn.h>+#include<net/red.h>++/* CHOKe stateless AQM for fair bandwidth allocation+=================================================++CHOKe(CHOoseandKeepforresponsiveflows,CHOoseandKillfor+unresponsiveflows)isavariantofREDthatpenalizesmisbehavingflowsbut+maintainsnoflowstate.ThedifferencefromREDisanadditionalstep+duringtheenqueuingprocess.Ifaveragequeuesizeisoverthe+lowthreshold(qmin),apacketischosenatrandomfromthequeue.+Ifboththenewandchosenpacketarefromthesameflow,both+aredropped.UnlikeRED,CHOKeisnotreallya"classful"qdiscbecauseit+needstoaccesspacketsinqueuerandomly.Ithasaminimalclass+interfacetoallowoverridingthebuiltinflowclassifierwith+filters.++Source:+R.Pan,B.Prabhakar,andK.Psounis,"CHOKe, A Stateless+ActiveQueueManagementSchemeforApproximatingFairBandwidthAllocation",+IEEEINFOCOM,2000.++A.Tang,J.Wang,S.Low,"Understanding CHOKe: Throughput and Spatial+Characteristics", IEEE/ACM Transactions on Networking, 2004++*/++structchoke_sched_data{+/* Parameters */+u32limit;+unsignedcharflags;++structred_parmsparms;+structred_statsstats;++/* Variables */+structtcf_proto*filter_list;+unsignedinthead;+unsignedinttail;+unsignedintholes;+unsignedinttab_mask;/* size - 1 */++structsk_buff**tab;+};++staticinlineunsignedintchoke_len(conststructchoke_sched_data*q)+{+return(q->tail-q->head)&q->tab_mask;+}++/* deliver a random number between 0 and N - 1 */+staticinlineu32random_N(unsignedintN)+{+returnreciprocal_divide(random32(),N);+}+++/* Select a packet at random from the queue in O(1) and handle holes */+staticstructsk_buff*choke_peek_random(structchoke_sched_data*q,+unsignedint*pidx)+{+structsk_buff*skb;+intretrys=3;++do{+*pidx=(q->head+random_N(choke_len(q)))&q->tab_mask;+skb=q->tab[*pidx];+if(skb)+returnskb;+}while(--retrys>0);++/* queue is has lots of holes use the head which is known to exist */+returnq->tab[*pidx=q->head];+}++/* Is ECN parameter configured */+staticinlineintuse_ecn(conststructchoke_sched_data*q)+{+returnq->flags&TC_RED_ECN;+}++/* Should packets over max just be dropped (versus marked) */+staticinlineintuse_harddrop(conststructchoke_sched_data*q)+{+returnq->flags&TC_RED_HARDDROP;+}++/* Move head pointer forward to skip over holes */+staticvoidchoke_zap_head_holes(structchoke_sched_data*q)+{+while(q->holes&&q->tab[q->head]==NULL){+q->head=(q->head+1)&q->tab_mask;+q->holes--;+}+}++/* Move tail pointer backwards to reuse holes */+staticvoidchoke_zap_tail_holes(structchoke_sched_data*q)+{+while(q->holes&&q->tab[q->tail-1]==NULL){+q->tail=(q->tail-1)&q->tab_mask;+q->holes--;+}+}++/* Drop packet from queue array by creating a "hole" */+staticvoidchoke_drop_by_idx(structchoke_sched_data*q,unsignedintidx)+{+q->tab[idx]=NULL;+q->holes++;++if(idx==q->head)+choke_zap_head_holes(q);+if(idx==q->tail)+choke_zap_tail_holes(q);+}++/* Classify flow using either:+1.pre-existingclassificationresultinskb+2.fastinternalclassification+3.useTCfilterbasedclassification+*/+staticinlineunsignedintchoke_classify(structsk_buff*skb,+structQdisc*sch,int*qerr)++{+structchoke_sched_data*q=qdisc_priv(sch);+structtcf_resultres;+intresult;++*qerr=NET_XMIT_SUCCESS|__NET_XMIT_BYPASS;++if(TC_H_MAJ(skb->priority)==sch->handle&&+TC_H_MIN(skb->priority)>0)+returnTC_H_MIN(skb->priority);++if(!q->filter_list)+returnskb_get_rxhash(skb);++result=tc_classify(skb,q->filter_list,&res);+if(result>=0){+#ifdef CONFIG_NET_CLS_ACT+switch(result){+caseTC_ACT_STOLEN:+caseTC_ACT_QUEUED:+*qerr=NET_XMIT_SUCCESS|__NET_XMIT_STOLEN;+caseTC_ACT_SHOT:+return0;+}+#endif+returnTC_H_MIN(res.classid);+}++return0;+}++staticintchoke_enqueue(structsk_buff*skb,structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);+structred_parms*p=&q->parms;+unsignedinthash;+intuninitialized_var(ret);++hash=choke_classify(skb,sch,&ret);+if(unlikely(!hash)){+if(ret&__NET_XMIT_BYPASS)+sch->qstats.drops++;+kfree_skb(skb);+returnret;+}++/* XXX add hash to qdisc_skb_cb? */+*(unsignedint*)(qdisc_skb_cb(skb)->data)=hash;++/* Compute average queue usage (see RED) */+p->qavg=red_calc_qavg(p,choke_len(q)-q->holes);+if(red_is_idling(p))+red_end_of_idle_period(p);++/* Is queue small? */+if(p->qavg<=p->qth_min)+p->qcount=-1;+else{+structsk_buff*oskb;+unsignedintidx;++/* Draw a packet at random from queue */+oskb=choke_peek_random(q,&idx);++/* Both packets from same flow ? */+if(*(unsignedint*)(qdisc_skb_cb(oskb)->data)==hash){+/* Drop both packets */+choke_drop_by_idx(q,idx);+qdisc_drop(oskb,sch);+gotocongestion_drop;+}++if(p->qavg>p->qth_max){+p->qcount=-1;++sch->qstats.overlimits++;+if(use_harddrop(q)||!use_ecn(q)||+!INET_ECN_set_ce(skb)){+q->stats.forced_drop++;+gotocongestion_drop;+}++q->stats.forced_mark++;+}++if(++p->qcount){+if(red_mark_probability(p,p->qavg)){+p->qcount=0;+p->qR=red_random(p);++sch->qstats.overlimits++;+if(!use_ecn(q)||!INET_ECN_set_ce(skb)){+q->stats.prob_drop++;+gotocongestion_drop;+}++q->stats.prob_mark++;+}+}else+p->qR=red_random(p);+}++/* Admit new packet */+if(likely(choke_len(q)<q->limit)){+q->tab[q->tail]=skb;+q->tail=(q->tail+1)&q->tab_mask;++sch->qstats.backlog+=qdisc_pkt_len(skb);+__qdisc_update_bstats(sch,qdisc_pkt_len(skb));+sch->q.qlen=choke_len(q)-q->holes;+returnNET_XMIT_SUCCESS;+}++q->stats.pdrop++;+sch->qstats.drops++;+kfree_skb(skb);+returnNET_XMIT_DROP;++congestion_drop:+qdisc_drop(skb,sch);+returnNET_XMIT_CN;+}++staticstructsk_buff*choke_dequeue(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);+structsk_buff*skb;++if(q->head==q->tail){+if(!red_is_idling(&q->parms))+red_start_of_idle_period(&q->parms);+returnNULL;+}+skb=q->tab[q->head];+q->tab[q->head]=NULL;/* not really needed */+q->head=(q->head+1)&q->tab_mask;+choke_zap_head_holes(q);+sch->qstats.backlog-=qdisc_pkt_len(skb);+sch->q.qlen=choke_len(q)-q->holes;++returnskb;+}++staticunsignedintchoke_drop(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);+unsignedintlen;++len=qdisc_queue_drop(sch);++if(len>0)+q->stats.other++;+else{+if(!red_is_idling(&q->parms))+red_start_of_idle_period(&q->parms);+}++returnlen;+}++staticvoidchoke_reset(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);++red_restart(&q->parms);+}++staticconststructnla_policychoke_policy[TCA_RED_MAX+1]={+[TCA_RED_PARMS]={.len=sizeof(structtc_red_qopt)},+[TCA_RED_STAB]={.len=RED_STAB_SIZE},+};+++staticvoidchoke_free(void*addr)+{+if(addr){+if(is_vmalloc_addr(addr))+vfree(addr);+else+kfree(addr);+}+}++staticintchoke_change(structQdisc*sch,structnlattr*opt)+{+structchoke_sched_data*q=qdisc_priv(sch);+structnlattr*tb[TCA_RED_MAX+1];+structtc_red_qopt*ctl;+interr;+structsk_buff**old=NULL;+unsignedintmask;++if(opt==NULL)+return-EINVAL;++err=nla_parse_nested(tb,TCA_RED_MAX,opt,choke_policy);+if(err<0)+returnerr;++if(tb[TCA_RED_PARMS]==NULL||+tb[TCA_RED_STAB]==NULL)+return-EINVAL;++ctl=nla_data(tb[TCA_RED_PARMS]);++mask=roundup_pow_of_two(ctl->limit+1)-1;+if(mask!=q->tab_mask){+structsk_buff**ntab=kcalloc(mask+1,sizeof(structsk_buff*),+GFP_KERNEL);+if(!ntab)+ntab=vzalloc((mask+1)*sizeof(structsk_buff*));+if(!ntab)+return-ENOMEM;+sch_tree_lock(sch);+old=q->tab;+if(old){+unsignedinttail=0;++while(q->head!=q->tail){+ntab[tail++]=q->tab[q->head];+q->head=(q->head+1)&q->tab_mask;+}+q->head=0;+q->tail=tail;+}+q->tab_mask=mask;+q->tab=ntab;+q->holes=0;+}else+sch_tree_lock(sch);+q->flags=ctl->flags;+q->limit=ctl->limit;++red_set_parms(&q->parms,ctl->qth_min,ctl->qth_max,ctl->Wlog,+ctl->Plog,ctl->Scell_log,+nla_data(tb[TCA_RED_STAB]));++if(q->head==q->tail)+red_end_of_idle_period(&q->parms);++sch_tree_unlock(sch);+choke_free(old);+return0;+}++staticintchoke_init(structQdisc*sch,structnlattr*opt)+{+returnchoke_change(sch,opt);+}++staticintchoke_dump(structQdisc*sch,structsk_buff*skb)+{+structchoke_sched_data*q=qdisc_priv(sch);+structnlattr*opts=NULL;+structtc_red_qoptopt={+.limit=q->limit,+.flags=q->flags,+.qth_min=q->parms.qth_min>>q->parms.Wlog,+.qth_max=q->parms.qth_max>>q->parms.Wlog,+.Wlog=q->parms.Wlog,+.Plog=q->parms.Plog,+.Scell_log=q->parms.Scell_log,+};++opts=nla_nest_start(skb,TCA_OPTIONS);+if(opts==NULL)+gotonla_put_failure;++NLA_PUT(skb,TCA_RED_PARMS,sizeof(opt),&opt);+returnnla_nest_end(skb,opts);++nla_put_failure:+nla_nest_cancel(skb,opts);+return-EMSGSIZE;+}++staticintchoke_dump_stats(structQdisc*sch,structgnet_dump*d)+{+structchoke_sched_data*q=qdisc_priv(sch);+structtc_red_xstatsst={+.early=q->stats.prob_drop+q->stats.forced_drop,+.pdrop=q->stats.pdrop,+.other=q->stats.other,+.marked=q->stats.prob_mark+q->stats.forced_mark,+};++returngnet_stats_copy_app(d,&st,sizeof(st));+}++staticvoidchoke_destroy(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);++tcf_destroy_chain(&q->filter_list);+choke_free(q->tab);+}++staticstructQdisc*choke_leaf(structQdisc*sch,unsignedlongarg)+{+returnNULL;+}++staticunsignedlongchoke_get(structQdisc*sch,u32classid)+{+return0;+}++staticvoidchoke_put(structQdisc*q,unsignedlongcl)+{+}++staticunsignedlongchoke_bind(structQdisc*sch,unsignedlongparent,+u32classid)+{+return0;+}++staticstructtcf_proto**choke_find_tcf(structQdisc*sch,unsignedlongcl)+{+structchoke_sched_data*q=qdisc_priv(sch);++if(cl)+returnNULL;+return&q->filter_list;+}++staticintchoke_dump_class(structQdisc*sch,unsignedlongcl,+structsk_buff*skb,structtcmsg*tcm)+{+tcm->tcm_handle|=TC_H_MIN(cl);+return0;+}++staticvoidchoke_walk(structQdisc*sch,structqdisc_walker*arg)+{+if(!arg->stop){+if(arg->fn(sch,1,arg)<0){+arg->stop=1;+return;+}+arg->count++;+}+}++staticconststructQdisc_class_opschoke_class_ops={+.leaf=choke_leaf,+.get=choke_get,+.put=choke_put,+.tcf_chain=choke_find_tcf,+.bind_tcf=choke_bind,+.unbind_tcf=choke_put,+.dump=choke_dump_class,+.walk=choke_walk,+};++staticstructsk_buff*choke_peek_head(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);++return(q->head!=q->tail)?q->tab[q->head]:NULL;+}++staticstructQdisc_opschoke_qdisc_ops__read_mostly={+.id="choke",+.priv_size=sizeof(structchoke_sched_data),++.enqueue=choke_enqueue,+.dequeue=choke_dequeue,+.peek=choke_peek_head,+.drop=choke_drop,+.init=choke_init,+.destroy=choke_destroy,+.reset=choke_reset,+.change=choke_change,+.dump=choke_dump,+.dump_stats=choke_dump_stats,+.owner=THIS_MODULE,+};++staticint__initchoke_module_init(void)+{+returnregister_qdisc(&choke_qdisc_ops);+}++staticvoid__exitchoke_module_exit(void)+{+unregister_qdisc(&choke_qdisc_ops);+}++module_init(choke_module_init)+module_exit(choke_module_exit)++MODULE_LICENSE("GPL");
From: Stephen Hemminger <hidden> Date: 2011-01-11 23:48:10
On Tue, 11 Jan 2011 07:34:10 +0100
Eric Dumazet [off-list ref] wrote:
Le mardi 11 janvier 2011 à 07:18 +0100, Eric Dumazet a écrit :
quoted
Le lundi 10 janvier 2011 à 17:10 -0800, Stephen Hemminger a écrit :
quoted
OK, put that in.
Thanks !
quoted
quoted
Here we missed :
q->tab = ntab;
We also need to change
.peek = qdisc_peek_head,
to
.peek = choke_peek_head,
static struct sk_buff *choke_peek_head(struct Qdisc *sch)
{
struct choke_sched_data *q = qdisc_priv(sch);
return (q->head != q->tail) ? q->tab[q->head] : NULL;
}
And to correctly work with CBQ (at least...), we need to update
sch->q.qlen = choke_len(q) - q->holes;
in dequeue() and enqueue()
Here is the version I successfully tested, with 30000 packets in
queue :)
qdisc choke 11: parent 1:11 limit 70000b min 10000b max 30000b ewma 1 Plog 16 Scell_log 11
Sent 62099201 bytes 112920 pkt (dropped 367712, overlimits 282668 requeues 0)
rate 21344Kbit 4851pps backlog 39877589b 30001p requeues 0
marked 0 early 282668 pdrop 0 other 0
Maybe we should take over one of the red counters for the probabilistic match drop.
From: Eric Dumazet <hidden> Date: 2011-01-12 00:04:05
Le mardi 11 janvier 2011 à 15:48 -0800, Stephen Hemminger a écrit :
On Tue, 11 Jan 2011 07:34:10 +0100
Eric Dumazet [off-list ref] wrote:
quoted
qdisc choke 11: parent 1:11 limit 70000b min 10000b max 30000b ewma 1 Plog 16 Scell_log 11
Sent 62099201 bytes 112920 pkt (dropped 367712, overlimits 282668 requeues 0)
rate 21344Kbit 4851pps backlog 39877589b 30001p requeues 0
marked 0 early 282668 pdrop 0 other 0
Maybe we should take over one of the red counters for the probabilistic match drop.
aka chokedrop ;)
Anyway, iproute2 should be tweaked a bit, since limit/min/max are
packets, not bytes like RED. It would be nice to print probability
too ;)
AFAIK, requeue counter is not anymore used (On leaves at least), we
could re-use it for chokedrop
----
qdisc choke 11: parent 1:11 limit 70000p min 10000p max 30000p proba 0.08
Sent 62099201 bytes 112920 pkt (dropped 367712, overlimits 282668 chokedrop 999)
rate 21344Kbit 4851pps backlog 39877589b 30001p chokedrop 999
marked 0 early 282668 pdrop 0 other 0
From: Eric Dumazet <hidden> Date: 2011-01-12 07:13:54
Hi Stephen, here is my v0.6 version :
- Added sanity checks before kcalloc()/kzalloc()
- Added a __GFP_NOWARN to kcalloc()
- Added call to qdisc_bstats_update() after commit bfe0d0298f2a67d94d5
(net_sched: factorize qdisc stats handling)
TODO :
- Added a stat specific update to track CHOKe probabilistic dual-drops
I temporarily use requeues counter to make sure our code works
qdisc choke 11: parent 1:11 limit 70000b min 10000b max 30000b ewma 1 Plog 16 Scell_log 11
Sent 236155665 bytes 429432 pkt (dropped 1644435, overlimits 1251933 requeues 196251)
rate 38800Kbit 8820pps backlog 124438905b 30001p requeues 196251
marked 0 early 1251933 pdrop 0 other 0
Thanks !
@@ -0,0 +1,540 @@+/*+*net/sched/sch_choke.cCHOKEscheduler+*+*Copyright(c)2011StephenHemminger<shemminger@vyatta.com>+*Copyright(c)2011EricDumazet<eric.dumazet@gmail.com>+*+*Thisprogramisfreesoftware;youcanredistributeitand/or+*modifyitunderthetermsoftheGNUGeneralPublicLicense+*version2aspublishedbytheFreeSoftwareFoundation.+*+*/++#include<linux/module.h>+#include<linux/types.h>+#include<linux/kernel.h>+#include<linux/skbuff.h>+#include<linux/reciprocal_div.h>+#include<net/pkt_sched.h>+#include<net/inet_ecn.h>+#include<net/red.h>++/* CHOKe stateless AQM for fair bandwidth allocation+=================================================++CHOKe(CHOoseandKeepforresponsiveflows,CHOoseandKillfor+unresponsiveflows)isavariantofREDthatpenalizesmisbehavingflowsbut+maintainsnoflowstate.ThedifferencefromREDisanadditionalstep+duringtheenqueuingprocess.Ifaveragequeuesizeisoverthe+lowthreshold(qmin),apacketischosenatrandomfromthequeue.+Ifboththenewandchosenpacketarefromthesameflow,both+aredropped.UnlikeRED,CHOKeisnotreallya"classful"qdiscbecauseit+needstoaccesspacketsinqueuerandomly.Ithasaminimalclass+interfacetoallowoverridingthebuiltinflowclassifierwith+filters.++Source:+R.Pan,B.Prabhakar,andK.Psounis,"CHOKe, A Stateless+ActiveQueueManagementSchemeforApproximatingFairBandwidthAllocation",+IEEEINFOCOM,2000.++A.Tang,J.Wang,S.Low,"Understanding CHOKe: Throughput and Spatial+Characteristics", IEEE/ACM Transactions on Networking, 2004++*/++structchoke_sched_data{+/* Parameters */+u32limit;+unsignedcharflags;++structred_parmsparms;+structred_statsstats;++/* Variables */+structtcf_proto*filter_list;+unsignedinthead;+unsignedinttail;+unsignedintholes;+unsignedinttab_mask;/* size - 1 */++structsk_buff**tab;+};++staticinlineunsignedintchoke_len(conststructchoke_sched_data*q)+{+return(q->tail-q->head)&q->tab_mask;+}++/* deliver a random number between 0 and N - 1 */+staticinlineu32random_N(unsignedintN)+{+returnreciprocal_divide(random32(),N);+}+++/* Select a packet at random from the queue in O(1) and handle holes */+staticstructsk_buff*choke_peek_random(structchoke_sched_data*q,+unsignedint*pidx)+{+structsk_buff*skb;+intretrys=3;++do{+*pidx=(q->head+random_N(choke_len(q)))&q->tab_mask;+skb=q->tab[*pidx];+if(skb)+returnskb;+}while(--retrys>0);++/* queue is has lots of holes use the head which is known to exist */+returnq->tab[*pidx=q->head];+}++/* Is ECN parameter configured */+staticinlineintuse_ecn(conststructchoke_sched_data*q)+{+returnq->flags&TC_RED_ECN;+}++/* Should packets over max just be dropped (versus marked) */+staticinlineintuse_harddrop(conststructchoke_sched_data*q)+{+returnq->flags&TC_RED_HARDDROP;+}++/* Move head pointer forward to skip over holes */+staticvoidchoke_zap_head_holes(structchoke_sched_data*q)+{+while(q->holes&&q->tab[q->head]==NULL){+q->head=(q->head+1)&q->tab_mask;+q->holes--;+}+}++/* Move tail pointer backwards to reuse holes */+staticvoidchoke_zap_tail_holes(structchoke_sched_data*q)+{+while(q->holes&&q->tab[q->tail-1]==NULL){+q->tail=(q->tail-1)&q->tab_mask;+q->holes--;+}+}++/* Drop packet from queue array by creating a "hole" */+staticvoidchoke_drop_by_idx(structchoke_sched_data*q,unsignedintidx)+{+q->tab[idx]=NULL;+q->holes++;++if(idx==q->head)+choke_zap_head_holes(q);+if(idx==q->tail)+choke_zap_tail_holes(q);+}++/* Classify flow using either:+1.pre-existingclassificationresultinskb+2.fastinternalclassification+3.useTCfilterbasedclassification+*/+staticinlineunsignedintchoke_classify(structsk_buff*skb,+structQdisc*sch,int*qerr)++{+structchoke_sched_data*q=qdisc_priv(sch);+structtcf_resultres;+intresult;++*qerr=NET_XMIT_SUCCESS|__NET_XMIT_BYPASS;++if(TC_H_MAJ(skb->priority)==sch->handle&&+TC_H_MIN(skb->priority)>0)+returnTC_H_MIN(skb->priority);++if(!q->filter_list)+returnskb_get_rxhash(skb);++result=tc_classify(skb,q->filter_list,&res);+if(result>=0){+#ifdef CONFIG_NET_CLS_ACT+switch(result){+caseTC_ACT_STOLEN:+caseTC_ACT_QUEUED:+*qerr=NET_XMIT_SUCCESS|__NET_XMIT_STOLEN;+caseTC_ACT_SHOT:+return0;+}+#endif+returnTC_H_MIN(res.classid);+}++return0;+}++staticintchoke_enqueue(structsk_buff*skb,structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);+structred_parms*p=&q->parms;+unsignedinthash;+intuninitialized_var(ret);++hash=choke_classify(skb,sch,&ret);+if(unlikely(!hash)){+if(ret&__NET_XMIT_BYPASS)+sch->qstats.drops++;+kfree_skb(skb);+returnret;+}++/* XXX add hash to qdisc_skb_cb? */+*(unsignedint*)(qdisc_skb_cb(skb)->data)=hash;++/* Compute average queue usage (see RED) */+p->qavg=red_calc_qavg(p,choke_len(q)-q->holes);+if(red_is_idling(p))+red_end_of_idle_period(p);++/* Is queue small? */+if(p->qavg<=p->qth_min)+p->qcount=-1;+else{+structsk_buff*oskb;+unsignedintidx;++/* Draw a packet at random from queue */+oskb=choke_peek_random(q,&idx);++/* Both packets from same flow ? */+if(*(unsignedint*)(qdisc_skb_cb(oskb)->data)==hash){+/* Drop both packets */+choke_drop_by_idx(q,idx);+qdisc_drop(oskb,sch);+sch->qstats.requeues++;+gotocongestion_drop;+}++if(p->qavg>p->qth_max){+p->qcount=-1;++sch->qstats.overlimits++;+if(use_harddrop(q)||!use_ecn(q)||+!INET_ECN_set_ce(skb)){+q->stats.forced_drop++;+gotocongestion_drop;+}++q->stats.forced_mark++;+}++if(++p->qcount){+if(red_mark_probability(p,p->qavg)){+p->qcount=0;+p->qR=red_random(p);++sch->qstats.overlimits++;+if(!use_ecn(q)||!INET_ECN_set_ce(skb)){+q->stats.prob_drop++;+gotocongestion_drop;+}++q->stats.prob_mark++;+}+}else+p->qR=red_random(p);+}++/* Admit new packet */+if(likely(choke_len(q)<q->limit)){+q->tab[q->tail]=skb;+q->tail=(q->tail+1)&q->tab_mask;++sch->qstats.backlog+=qdisc_pkt_len(skb);+qdisc_bstats_update(sch,skb);+sch->q.qlen=choke_len(q)-q->holes;+returnNET_XMIT_SUCCESS;+}++q->stats.pdrop++;+sch->qstats.drops++;+kfree_skb(skb);+returnNET_XMIT_DROP;++congestion_drop:+qdisc_drop(skb,sch);+returnNET_XMIT_CN;+}++staticstructsk_buff*choke_dequeue(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);+structsk_buff*skb;++if(q->head==q->tail){+if(!red_is_idling(&q->parms))+red_start_of_idle_period(&q->parms);+returnNULL;+}+skb=q->tab[q->head];+q->tab[q->head]=NULL;/* not really needed */+q->head=(q->head+1)&q->tab_mask;+choke_zap_head_holes(q);+sch->qstats.backlog-=qdisc_pkt_len(skb);+sch->q.qlen=choke_len(q)-q->holes;++returnskb;+}++staticunsignedintchoke_drop(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);+unsignedintlen;++len=qdisc_queue_drop(sch);++if(len>0)+q->stats.other++;+else{+if(!red_is_idling(&q->parms))+red_start_of_idle_period(&q->parms);+}++returnlen;+}++staticvoidchoke_reset(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);++red_restart(&q->parms);+}++staticconststructnla_policychoke_policy[TCA_RED_MAX+1]={+[TCA_RED_PARMS]={.len=sizeof(structtc_red_qopt)},+[TCA_RED_STAB]={.len=RED_STAB_SIZE},+};+++staticvoidchoke_free(void*addr)+{+if(addr){+if(is_vmalloc_addr(addr))+vfree(addr);+else+kfree(addr);+}+}++staticintchoke_change(structQdisc*sch,structnlattr*opt)+{+structchoke_sched_data*q=qdisc_priv(sch);+structnlattr*tb[TCA_RED_MAX+1];+structtc_red_qopt*ctl;+interr;+structsk_buff**old=NULL;+unsignedintmask;++if(opt==NULL)+return-EINVAL;++err=nla_parse_nested(tb,TCA_RED_MAX,opt,choke_policy);+if(err<0)+returnerr;++if(tb[TCA_RED_PARMS]==NULL||+tb[TCA_RED_STAB]==NULL)+return-EINVAL;++ctl=nla_data(tb[TCA_RED_PARMS]);++mask=roundup_pow_of_two(ctl->limit+1)-1;+/* limit queue size to one MB */+if(mask+1>(1U<<20)/sizeof(structsk_buff*))+return-EINVAL;+if(mask!=q->tab_mask){+structsk_buff**ntab=kcalloc(mask+1,sizeof(structsk_buff*),+GFP_KERNEL|__GFP_NOWARN);+if(!ntab)+ntab=vzalloc((mask+1)*sizeof(structsk_buff*));+if(!ntab)+return-ENOMEM;+sch_tree_lock(sch);+old=q->tab;+if(old){+unsignedinttail=0;++while(q->head!=q->tail){+ntab[tail++]=q->tab[q->head];+q->head=(q->head+1)&q->tab_mask;+}+q->head=0;+q->tail=tail;+}+q->tab_mask=mask;+q->tab=ntab;+q->holes=0;+}else+sch_tree_lock(sch);+q->flags=ctl->flags;+q->limit=ctl->limit;++red_set_parms(&q->parms,ctl->qth_min,ctl->qth_max,ctl->Wlog,+ctl->Plog,ctl->Scell_log,+nla_data(tb[TCA_RED_STAB]));++if(q->head==q->tail)+red_end_of_idle_period(&q->parms);++sch_tree_unlock(sch);+choke_free(old);+return0;+}++staticintchoke_init(structQdisc*sch,structnlattr*opt)+{+returnchoke_change(sch,opt);+}++staticintchoke_dump(structQdisc*sch,structsk_buff*skb)+{+structchoke_sched_data*q=qdisc_priv(sch);+structnlattr*opts=NULL;+structtc_red_qoptopt={+.limit=q->limit,+.flags=q->flags,+.qth_min=q->parms.qth_min>>q->parms.Wlog,+.qth_max=q->parms.qth_max>>q->parms.Wlog,+.Wlog=q->parms.Wlog,+.Plog=q->parms.Plog,+.Scell_log=q->parms.Scell_log,+};++opts=nla_nest_start(skb,TCA_OPTIONS);+if(opts==NULL)+gotonla_put_failure;++NLA_PUT(skb,TCA_RED_PARMS,sizeof(opt),&opt);+returnnla_nest_end(skb,opts);++nla_put_failure:+nla_nest_cancel(skb,opts);+return-EMSGSIZE;+}++staticintchoke_dump_stats(structQdisc*sch,structgnet_dump*d)+{+structchoke_sched_data*q=qdisc_priv(sch);+structtc_red_xstatsst={+.early=q->stats.prob_drop+q->stats.forced_drop,+.pdrop=q->stats.pdrop,+.other=q->stats.other,+.marked=q->stats.prob_mark+q->stats.forced_mark,+};++returngnet_stats_copy_app(d,&st,sizeof(st));+}++staticvoidchoke_destroy(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);++tcf_destroy_chain(&q->filter_list);+choke_free(q->tab);+}++staticstructQdisc*choke_leaf(structQdisc*sch,unsignedlongarg)+{+returnNULL;+}++staticunsignedlongchoke_get(structQdisc*sch,u32classid)+{+return0;+}++staticvoidchoke_put(structQdisc*q,unsignedlongcl)+{+}++staticunsignedlongchoke_bind(structQdisc*sch,unsignedlongparent,+u32classid)+{+return0;+}++staticstructtcf_proto**choke_find_tcf(structQdisc*sch,unsignedlongcl)+{+structchoke_sched_data*q=qdisc_priv(sch);++if(cl)+returnNULL;+return&q->filter_list;+}++staticintchoke_dump_class(structQdisc*sch,unsignedlongcl,+structsk_buff*skb,structtcmsg*tcm)+{+tcm->tcm_handle|=TC_H_MIN(cl);+return0;+}++staticvoidchoke_walk(structQdisc*sch,structqdisc_walker*arg)+{+if(!arg->stop){+if(arg->fn(sch,1,arg)<0){+arg->stop=1;+return;+}+arg->count++;+}+}++staticconststructQdisc_class_opschoke_class_ops={+.leaf=choke_leaf,+.get=choke_get,+.put=choke_put,+.tcf_chain=choke_find_tcf,+.bind_tcf=choke_bind,+.unbind_tcf=choke_put,+.dump=choke_dump_class,+.walk=choke_walk,+};++staticstructsk_buff*choke_peek_head(structQdisc*sch)+{+structchoke_sched_data*q=qdisc_priv(sch);++return(q->head!=q->tail)?q->tab[q->head]:NULL;+}++staticstructQdisc_opschoke_qdisc_ops__read_mostly={+.id="choke",+.priv_size=sizeof(structchoke_sched_data),++.enqueue=choke_enqueue,+.dequeue=choke_dequeue,+.peek=choke_peek_head,+.drop=choke_drop,+.init=choke_init,+.destroy=choke_destroy,+.reset=choke_reset,+.change=choke_change,+.dump=choke_dump,+.dump_stats=choke_dump_stats,+.owner=THIS_MODULE,+};++staticint__initchoke_module_init(void)+{+returnregister_qdisc(&choke_qdisc_ops);+}++staticvoid__exitchoke_module_exit(void)+{+unregister_qdisc(&choke_qdisc_ops);+}++module_init(choke_module_init)+module_exit(choke_module_exit)++MODULE_LICENSE("GPL");
From: Stephen Hemminger <hidden> Date: 2011-01-12 17:27:42
On Wed, 12 Jan 2011 08:13:48 +0100
Eric Dumazet [off-list ref] wrote:
Hi Stephen, here is my v0.6 version :
- Added sanity checks before kcalloc()/kzalloc()
- Added a __GFP_NOWARN to kcalloc()
- Added call to qdisc_bstats_update() after commit bfe0d0298f2a67d94d5
(net_sched: factorize qdisc stats handling)
TODO :
- Added a stat specific update to track CHOKe probabilistic dual-drops
I temporarily use requeues counter to make sure our code works
I am going to redo stats and config.
Should thresholds be packet or byte based? I prefer packet
Also leaning towards merging qmax and qlimit together.
From: Eric Dumazet <hidden> Date: 2011-01-12 17:33:15
Le mercredi 12 janvier 2011 à 09:27 -0800, Stephen Hemminger a écrit :
On Wed, 12 Jan 2011 08:13:48 +0100
Eric Dumazet [off-list ref] wrote:
quoted
Hi Stephen, here is my v0.6 version :
- Added sanity checks before kcalloc()/kzalloc()
- Added a __GFP_NOWARN to kcalloc()
- Added call to qdisc_bstats_update() after commit bfe0d0298f2a67d94d5
(net_sched: factorize qdisc stats handling)
TODO :
- Added a stat specific update to track CHOKe probabilistic dual-drops
I temporarily use requeues counter to make sure our code works
I am going to redo stats and config.
Should thresholds be packet or byte based? I prefer packet
Also leaning towards merging qmax and qlimit together.
I believe CHOKe spirit is per packet, I agree with you.
(This makes the tab[] array sizing directly depends on ctl->limit, not
on a computation ctl->limit/smallest_packet_size)
Not sure we can merge qmax and qlimit, are you sure its OK with CHOKe
paper and experimental results ?
(Sorry I cant spend too much time right now to check this point)