pkt_sched: add DRR scheduler
Subsystems (3)
commit 9a2114f922015699b3b8402941f9e444f4a84369
Author: Patrick McHardy <[email protected]> Date: Wed Nov 19 15:25:03 2008 +0100
pkt_sched: add DRR scheduler
Add classful DRR scheduler as a more flexible replacement for SFQ.
The main difference to the algorithm described in "Efficient Fair Queueing using Deficit Round Robin" is that this implementation doesn't drop packets from the longest queue on overrun because its classful and limits are handled by each individual child qdisc.
include/linux/pkt_sched.h +16 -0
@@ -500,4 +500,20 @@ struct tc_netem_corrupt#define NETEM_DIST_SCALE 8192 +/* DRR */ + +enum +{ +TCA_DRR_UNSPEC , +TCA_DRR_QUANTUM , + __TCA_DRR_MAX +}; + +#define TCA_DRR_MAX (__TCA_DRR_MAX - 1) + +struct tc_drr_stats +{ +u32 deficit; +}; +#endif
net/sched/Kconfig +11 -0
@@ -194,6 +194,17 @@ config NET_SCH_NETEM If unsure, say N. +config NET_SCH_DRR + tristate "Deficit round robing scheduler (DRR)" + help + Say Y here if you want to use the Deficit round robin (DRR) packet + scheduling algorithm. + + To compile this driver as a module, choose M here: the module + will be called sch_drr. + + If unsure, say N. + config NET_SCH_INGRESS tristate "Ingress Qdisc" depends on NET_CLS_ACT
net/sched/Makefile +1 -0
@@ -30,6 +30,7 @@ obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o obj-$(CONFIG_NET_SCH_MULTIQ) += sch_multiq.o obj-$(CONFIG_NET_SCH_ATM) += sch_atm.o obj-$(CONFIG_NET_SCH_NETEM) += sch_netem.o +obj-$(CONFIG_NET_SCH_DRR) += sch_drr.o obj-$(CONFIG_NET_CLS_U32) += cls_u32.o obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o obj-$(CONFIG_NET_CLS_FW) += cls_fw.o
net/sched/sch_drr.c new file +505 -0
505 lines not on this page · raw message
On 19-11-2008 15:26, Patrick McHardy wrote:
...
net/sched/Kconfig config NET_SCH_NETEM · line 198
Quoted 10 lines
net/sched/Kconfig +2
-0@@ -194,6 +194,17 @@ config NET_SCH_NETEM If unsure, say N. +config NET_SCH_DRR + tristate "Deficit round robing scheduler (DRR)"
- tristate "Deficit round robing scheduler (DRR)"
+ tristate "Deficit Round Robin scheduler (DRR)"
net/sched/Kconfig config NET_SCH_NETEM · line 200
Quoted 2 lines
net/sched/Kconfig +2
-0+ help + Say Y here if you want to use the Deficit round robin (DRR) packet
- Say Y here if you want to use the Deficit round robin (DRR) packet
+ Say Y here if you want to use the Deficit Round Robin (DRR) packet
net/sched/Kconfig config NET_SCH_NETEM · line 201
Quoted 1 line
net/sched/Kconfig +1
-0+ scheduling algorithm.
Looks OK to me except checkpatch and documentation/example (any... even at the beginning of the program).
Jarek P.
Quoted 10 lines · Jarek Poplawski
Review
Quoted 3 lines · Patrick McHardy
(quoted diff) +2
-0+config NET_SCH_DRR + tristate "Deficit round robing scheduler (DRR)"- tristate "Deficit round robing scheduler (DRR)"
+ tristate "Deficit Round Robin scheduler (DRR)"
Thanks, I've fixed the typos.
Quoted 2 lines
Looks OK to me except checkpatch and documentation/example (any... even at the beginning of the program).
And the checkpatch errors.
Documentation for this in the kernel tree seems pretty useless though.
commit c6e712b1cf9446096159414cb9d7f8c58effe92f
Author: Patrick McHardy <[email protected]> Date: Thu Nov 20 12:39:18 2008 +0100
pkt_sched: add DRR scheduler
Add classful DRR scheduler as a more flexible replacement for SFQ.
The main difference to the algorithm described in "Efficient Fair Queueing using Deficit Round Robin" is that this implementation doesn't drop packets from the longest queue on overrun because its classful and limits are handled by each individual child qdisc.
include/linux/pkt_sched.h +16 -0
@@ -500,4 +500,20 @@ struct tc_netem_corrupt#define NETEM_DIST_SCALE 8192 +/* DRR */ + +enum +{ +TCA_DRR_UNSPEC , +TCA_DRR_QUANTUM , + __TCA_DRR_MAX +}; + +#define TCA_DRR_MAX (__TCA_DRR_MAX - 1) + +struct tc_drr_stats +{ +u32 deficit; +}; +#endif
net/sched/Kconfig +11 -0
@@ -194,6 +194,17 @@ config NET_SCH_NETEM If unsure, say N. +config NET_SCH_DRR + tristate "Deficit Round Robin scheduler (DRR)" + help + Say Y here if you want to use the Deficit Round Robin (DRR) packet + scheduling algorithm. + + To compile this driver as a module, choose M here: the module + will be called sch_drr. + + If unsure, say N. + config NET_SCH_INGRESS tristate "Ingress Qdisc" depends on NET_CLS_ACT
net/sched/Makefile +1 -0
@@ -30,6 +30,7 @@ obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o obj-$(CONFIG_NET_SCH_MULTIQ) += sch_multiq.o obj-$(CONFIG_NET_SCH_ATM) += sch_atm.o obj-$(CONFIG_NET_SCH_NETEM) += sch_netem.o +obj-$(CONFIG_NET_SCH_DRR) += sch_drr.o obj-$(CONFIG_NET_CLS_U32) += cls_u32.o obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o obj-$(CONFIG_NET_CLS_FW) += cls_fw.o
net/sched/sch_drr.c new file +505 -0
505 lines not on this page · raw message
On Thu, Nov 20, 2008 at 12:42:58PM +0100, Patrick McHardy wrote:
...
Quoted 1 line
Documentation for this in the kernel tree seems pretty useless though.
Right! What about iproute2 tree? (examples/?)
Jarek P.
Quoted 8 lines · Jarek Poplawski
On Thu, Nov 20, 2008 at 12:42:58PM +0100, Patrick McHardy wrote:
...Quoted 2 lines
Documentation for this in the kernel tree seems pretty useless though.
Right! What about iproute2 tree? (examples/?)
Yes, I'll send something when I find some time (manpage probably).
Its trivial to use though, these is only a single optional parameter, the quantum.
On Thu, Nov 20, 2008 at 12:58:11PM +0100, Patrick McHardy wrote:
...
Quoted 2 lines
Its trivial to use though, these is only a single optional parameter, the quantum.
OK, but if you advertise this as a "flexible replacement for SFQ" sb. could be mislead, so I mean at least a simple example of such replacement.
Jarek P.
From: Patrick McHardy <[email protected]>
Date: Thu, 20 Nov 2008 12:42:58 +0100
Quoted 6 lines
commit
c6e712b1cf9446096159414cb9d7f8c58effe92fAuthor: Patrick McHardy <[email protected]> Date: Thu Nov 20 12:39:18 2008 +0100
pkt_sched: add DRR scheduler
Quoted 8 lines
Add classful DRR scheduler as a more flexible replacement for SFQ.
The main difference to the algorithm described in "Efficient Fair Queueing using Deficit Round Robin" is that this implementation doesn't drop packets from the longest queue on overrun because its classful and limits are handled by each individual child qdisc.
Signed-off-by Patrick McHardy <[email protected]>
Applied, thanks Patrick.
A small oversight:
----------------->pkt_sched: sch_drr: Fix qlen in drr_drop()
---
net/sched/sch_drr.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
net/sched/sch_drr.c +1 -0
@@ -406,6 +406,7 @@ static unsigned int drr_drop(struct Qdisc *sch)if (cl->qdisc->ops->drop) { len = cl->qdisc->ops->drop (cl->qdisc);if (len >0 ) { + sch->q.qlen--;if (cl->qdisc->q.qlen ==0 )list_del (&cl->alist);return len;
Patrick McHardy
Acked-byQuoted 5 lines · Jarek Poplawski
A small oversight:
----------------->pkt_sched:sch_drr: Fix qlen indrr_drop()Signed-off-by Jarek Poplawski <[email protected]>
Good catch, thanks.
From: Patrick McHardy <[email protected]>
Date: Fri, 21 Nov 2008 13:36:21 +0100
Quoted 10 lines
Quoted 5 lines · Jarek Poplawski
A small oversight:
----------------->pkt_sched:sch_drr: Fix qlen indrr_drop()Signed-off-by Jarek Poplawski <[email protected]>Good catch, thanks.
Acked-by Patrick McHardy <[email protected]>
Applied, thanks.
PATCH 1/1 pkt_sched: sch_drr: Fix drr_dequeue() loop
In reply to Patrick McHardypkt_sched: sch_drr: Fix loop in drr_dequeue
If all child qdiscs of sch_drr are non-work-conserving (e.g. sch_tbf) drr_dequeue() will busy-loop waiting for skbs instead of leaving the job for a watchdog. Checking for list_empty() in each loop isn't necessary either, because this can never be true exept the first time.
---
net/sched/sch_drr.c | 24 +++++++++++++++++++++--- 1 files changed, 21 insertions(+), 3 deletions(-)
net/sched/sch_drr.c +21 -3
@@ -369,12 +369,17 @@ static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch)static struct sk_buff *drr_dequeue (struct Qdisc *sch) {struct drr_sched *q =qdisc_priv (sch); -struct drr_class *cl; +struct drr_class *cl, *cl_first;struct sk_buff *skb;unsigned int len; +bool skb_waiting = false; -while (!list_empty (&q->active)) { - cl = list_first_entry(&q->active, struct drr_class, alist); +if (list_empty (&q->active)) +return NULL ; + + cl_first = cl = list_first_entry(&q->active, struct drr_class, alist); + +while (1 ) { skb = cl->qdisc->ops->peek (cl->qdisc);if (skb ==NULL ) goto skip;
@@ -390,9 +395,22 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch) } cl->deficit += cl->quantum; + skb_waiting = true; skip:list_move_tail (&cl->alist, &q->active); + cl = list_first_entry(&q->active, struct drr_class, alist); + +if (cl == cl_first) { +if (skb_waiting) { +/* next round of deficit refilling */ + skb_waiting = false; + }else { +/* all qdiscs are non-work-conserving! */ +break ; + } + } } +return NULL ; }
PATCH v2 1/1 pkt_sched: sch_drr: Fix drr_dequeue() loop
In reply to Patrick McHardy(Changelog fixed only)
pkt_sched: sch_drr: Fix drr_dequeue() loop
If all child qdiscs of sch_drr are non-work-conserving (e.g. sch_tbf) drr_dequeue() will busy-loop waiting for skbs instead of leaving the job for a watchdog. Checking for list_empty() in each loop isn't necessary either, because this can never be true except the first time.
---
net/sched/sch_drr.c | 24 +++++++++++++++++++++--- 1 files changed, 21 insertions(+), 3 deletions(-)
net/sched/sch_drr.c +21 -3
@@ -369,12 +369,17 @@ static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch)static struct sk_buff *drr_dequeue (struct Qdisc *sch) {struct drr_sched *q =qdisc_priv (sch); -struct drr_class *cl; +struct drr_class *cl, *cl_first;struct sk_buff *skb;unsigned int len; +bool skb_waiting = false; -while (!list_empty (&q->active)) { - cl = list_first_entry(&q->active, struct drr_class, alist); +if (list_empty (&q->active)) +return NULL ; + + cl_first = cl = list_first_entry(&q->active, struct drr_class, alist); + +while (1 ) { skb = cl->qdisc->ops->peek (cl->qdisc);if (skb ==NULL ) goto skip;
@@ -390,9 +395,22 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch) } cl->deficit += cl->quantum; + skb_waiting = true; skip:list_move_tail (&cl->alist, &q->active); + cl = list_first_entry(&q->active, struct drr_class, alist); + +if (cl == cl_first) { +if (skb_waiting) { +/* next round of deficit refilling */ + skb_waiting = false; + }else { +/* all qdiscs are non-work-conserving! */ +break ; + } + } } +return NULL ; }
Re: [PATCH v2] pkt_sched: sch_drr: Fix drr_dequeue() loop
Quoted 8 lines · Jarek Poplawski
(Changelog fixed only)
pkt_sched:sch_drr: Fixdrr_dequeue()loopIf all child qdiscs of
sch_drrare non-work-conserving (e.g.sch_tbf)drr_dequeue()will busy-loop waiting for skbs instead of leaving the job for a watchdog. Checking forlist_empty()in each loop isn't necessary either, because this can never be true except the first time.
Thanks for the report. I don't like to overcomplicate treatment of this broken configuration though, so this patch simply returns NULL when the inner qdiscs is non-work-conserving.
commit f995e708c778d5c3201d769ffae636f6941654ed
Author: Patrick McHardy <[email protected]> Date: Mon Nov 24 13:13:16 2008 +0100
pkt_sched: sch_drr: fix drr_dequeue loop()
Jarek Poplawski points out:
If all child qdiscs of sch_drr are non-work-conserving (e.g. sch_tbf) drr_dequeue() will busy-loop waiting for skbs instead of leaving the job for a watchdog. Checking for list_empty() in each loop isn't necessary either, because this can never be true except the first time.
Using non-work-conserving qdiscs as children of DRR makes no sense, simply bail out in that case.
net/sched/sch_drr.c +5 -3
@@ -373,11 +373,13 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch)struct sk_buff *skb;unsigned int len; -while (!list_empty (&q->active)) { +if (list_empty (&q->active)) + goto out; +while (1 ) { cl = list_first_entry(&q->active, struct drr_class, alist); skb = cl->qdisc->ops->peek (cl->qdisc);if (skb ==NULL ) - goto skip; + goto out; len =qdisc_pkt_len (skb);if (len <= cl->deficit) {
@@ -390,9 +392,9 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch) } cl->deficit += cl->quantum; -skip:list_move_tail (&cl->alist, &q->active); } +out:return NULL ; }
Re: [PATCH v2] pkt_sched: sch_drr: Fix drr_dequeue() loop
Quoted 5 lines · Patrick McHardy
Quoted 4 lines · Jarek Poplawski
(Changelog fixed only)
pkt_sched:sch_drr: Fixdrr_dequeue()loop
Quoted 8 lines
Quoted 4 lines
If all child qdiscs of
sch_drrare non-work-conserving (e.g.sch_tbf)drr_dequeue()will busy-loop waiting for skbs instead of leaving the job for a watchdog. Checking forlist_empty()in each loop isn't necessary either, because this can never be true except the first time.Thanks for the report. I don't like to overcomplicate treatment of this broken configuration though, so this patch simply returns NULL when the inner qdiscs is non-work-conserving.
Hmm... I don't agree with treating this as broken (IIRC Denys Fedoryshchenko wrote about some case when he prefers TBF over HTB, and with DRR this could be an interesting alternative), but of course you are the author and I respect your decision.
Thanks,
Jarek P.
Re: [PATCH v2] pkt_sched: sch_drr: Fix drr_dequeue() loop
Quoted 10 lines · Jarek Poplawski
Quoted 9 lines · Patrick McHardy
Quoted 8 lines · Jarek Poplawski
(Changelog fixed only)
pkt_sched:sch_drr: Fixdrr_dequeue()loopIf all child qdiscs of
sch_drrare non-work-conserving (e.g.sch_tbf)drr_dequeue()will busy-loop waiting for skbs instead of leaving the job for a watchdog. Checking forlist_empty()in each loop isn't necessary either, because this can never be true except the first time.
Quoted 8 lines
Quoted 3 lines
Thanks for the report. I don't like to overcomplicate treatment of this broken configuration though, so this patch simply returns NULL when the inner qdiscs is non-work-conserving.
Hmm... I don't agree with treating this as broken (IIRC Denys Fedoryshchenko wrote about some case when he prefers TBF over HTB, and with DRR this could be an interesting alternative), but of course you are the author and I respect your decision.
TBF with an inner DRR is fine. The other way around is broken in the sense that the behaviour is undefined.
Re: [PATCH v2] pkt_sched: sch_drr: Fix drr_dequeue() loop
On Mon, Nov 24, 2008 at 01:38:48PM +0100, Patrick McHardy wrote:
...
Quoted 2 lines
TBF with an inner DRR is fine. The other way around is broken in the sense that the behaviour is undefined.
IMHO, this other way (e.g. a class with TBF per user), should work too.
BTW, since this "broken" config isn't very apparent, maybe you should add some warning?
Jarek P.
Re: [PATCH v2] pkt_sched: sch_drr: Fix drr_dequeue() loop
Quoted 6 lines · Jarek Poplawski
On Mon, Nov 24, 2008 at 01:38:48PM +0100, Patrick McHardy wrote:
...Quoted 2 lines
TBF with an inner DRR is fine. The other way around is broken in the sense that the behaviour is undefined.
IMHO, this other way (e.g. a class with TBF per user), should work too.
The behaviour undefined, so what does "work too" mean in this context?
The main question is: what should be done with the class when it throttles?
You suggest moving it to the end of the active list. Should its deficit be recharged in that case? Possible no because it didn't send packets - but then again it might have handed out *some* packets (less than the deficit) before it started throttling. Both ways would introduce unfairness.
What could be done without harming the algorithm is to treat throttled classes as inactive until they become unthrottled again, meaning they would be added to the end of the active list with a full deficit. But we have no indication for specific classes, unthrottling simply triggers another dequeue of the root, so the implementation would get quite complicated, leaving alone the fact that each TBF would potentially start its own watchdog, causing excessive wakeups.
And I don't see much use for this, what is the advantage over using HTB or HFSC?
Quoted 2 lines
BTW, since this "broken" config isn't very apparent, maybe you should add some warning?
Yes, but I don't want to add this to the ->dequeue() path. It belongs in the ->init() path and we currently don't have enough information in there.
Re: [PATCH v2] pkt_sched: sch_drr: Fix drr_dequeue() loop
Quoted 27 lines · Patrick McHardy
Quoted 6 lines · Jarek Poplawski
On Mon, Nov 24, 2008 at 01:38:48PM +0100, Patrick McHardy wrote:
...Quoted 2 lines
TBF with an inner DRR is fine. The other way around is broken in the sense that the behaviour is undefined.
IMHO, this other way (e.g. a class with TBF per user), should work too.
The behaviour undefined, so what does "work too" mean in this context?
The main question is: what should be done with the class when it throttles?
You suggest moving it to the end of the active list. Should its deficit be recharged in that case? Possible no because it didn't send packets - but then again it might have handed out *some* packets (less than the deficit) before it started throttling. Both ways would introduce unfairness.
What could be done without harming the algorithm is to treat throttled classes as inactive until they become unthrottled again, meaning they would be added to the end of the active list with a full deficit. But we have no indication for specific classes, unthrottling simply triggers another dequeue of the root, so the implementation would get quite complicated, leaving alone the fact that each TBF would potentially start its own watchdog, causing excessive wakeups.
Quoted 2 lines
And I don't see much use for this, what is the advantage over using HTB or HFSC?
Probably no advantage, if you now these things... or for testing. But I like to give users a choice, at least if it's not obviously wrong. Of course, if you think this harms the proper configs with too much overhead, then there is no question we should forget about this.
Jarek P.
David Miller
maintainerRe: [PATCH v2] pkt_sched: sch_drr: Fix drr_dequeue() loop
From: Jarek Poplawski <[email protected]>
Date: Mon, 24 Nov 2008 13:45:16 +0000
Quoted 28 lines · Patrick McHardy
Quoted 27 lines
Quoted 6 lines · Jarek Poplawski
On Mon, Nov 24, 2008 at 01:38:48PM +0100, Patrick McHardy wrote:
...Quoted 2 lines
TBF with an inner DRR is fine. The other way around is broken in the sense that the behaviour is undefined.
IMHO, this other way (e.g. a class with TBF per user), should work too.
The behaviour undefined, so what does "work too" mean in this context?
The main question is: what should be done with the class when it throttles?
You suggest moving it to the end of the active list. Should its deficit be recharged in that case? Possible no because it didn't send packets - but then again it might have handed out *some* packets (less than the deficit) before it started throttling. Both ways would introduce unfairness.
What could be done without harming the algorithm is to treat throttled classes as inactive until they become unthrottled again, meaning they would be added to the end of the active list with a full deficit. But we have no indication for specific classes, unthrottling simply triggers another dequeue of the root, so the implementation would get quite complicated, leaving alone the fact that each TBF would potentially start its own watchdog, causing excessive wakeups.
Quoted 7 lines
Quoted 2 lines
And I don't see much use for this, what is the advantage over using HTB or HFSC?
Probably no advantage, if you now these things... or for testing. But I like to give users a choice, at least if it's not obviously wrong. Of course, if you think this harms the proper configs with too much overhead, then there is no question we should forget about this.
Things seem to have settled, thus I have applied Patrick's version of the fix.
But I encourage people to add the necessary framework such that such unwanted configurations can be in fact detected at ->init() time and thus properly warned about.
Silent packet dropping really upsets users.
Re: [PATCH v2] pkt_sched: sch_drr: Fix drr_dequeue() loop
Quoted 2 lines · David Miller
Things seem to have settled, thus I have applied Patrick's version of the fix.
Thanks.
Quoted 5 lines
But I encourage people to add the necessary framework such that such unwanted configurations can be in fact detected at ->init() time and thus properly warned about.
Silent packet dropping really upsets users.
The packets won't be dropped, the qdisc will simply wait until the throttled inner qdisc becomes active again.
Refusing incorrect changes in ->init() is not easy because at least HFSC can switch between work-conserving and non-work-conserving through ->change(), so the upper qdisc would have to be able to perform validation before the change is made. This is additionally complicated by the fact that (in case of HFSC) it can't be determined by looking only at the class that is currently changed, the use of a upper-limit curve on any class makes it non-work-conserving.
Perhaps its best to ignore this special case and always treat HFSC as non-work-conserving, I haven't seen a configuration which uses a hierarchical qdisc as inner qdisc so far.