From: Eric Dumazet <hidden> Date: 2016-06-12 23:21:50
From: Eric Dumazet <edumazet@google.com>
At Qdisc creation or change time, prio_tune() creates missing
pfifo qdiscs but does not return an error code if one
qdisc could not be allocated.
Leaving a qdisc in non operational state without telling user
anything about this problem is not good.
Also, testing if we replace something different than noop_qdisc
a second time makes no sense so I removed useless code.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/sched/sch_prio.c | 32 ++++++++++++--------------------
1 file changed, 12 insertions(+), 20 deletions(-)
From: David Miller <davem@davemloft.net> Date: 2016-06-13 01:57:17
From: Eric Dumazet <redacted>
Date: Sun, 12 Jun 2016 16:21:47 -0700
From: Eric Dumazet <edumazet@google.com>
At Qdisc creation or change time, prio_tune() creates missing
pfifo qdiscs but does not return an error code if one
qdisc could not be allocated.
Leaving a qdisc in non operational state without telling user
anything about this problem is not good.
Also, testing if we replace something different than noop_qdisc
a second time makes no sense so I removed useless code.
Signed-off-by: Eric Dumazet <edumazet@google.com>
From: Eric Dumazet <hidden> Date: 2016-06-13 04:29:21
On Sun, 2016-06-12 at 20:45 -0700, Cong Wang wrote:
On Sun, Jun 12, 2016 at 4:21 PM, Eric Dumazet [off-list ref] wrote:
quoted
+ struct Qdisc *child;
+
+ if (q->queues[i] != &noop_qdisc)
+ continue;
+
+ child = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
+ TC_H_MAKE(sch->handle, i + 1));
+ if (!child)
+ return -ENOMEM;
Since this is inside a loop, shouldn't we kfree the previous child
creations when we fail?
You're right.
prio_init() needs to do the cleanup, as prio_destroy() wont be called
from qdisc_create()
I am testing a fix with fault injection.
Thanks.
From: Eric Dumazet <hidden> Date: 2016-06-13 05:03:54
From: Eric Dumazet <edumazet@google.com>
Now prio_init() can return -ENOMEM, it also has to make sure
any allocated qdisc are freed, since the caller (qdisc_create()) wont
call ->destroy() handler for us.
Fixes: cbdf45116478 ("net_sched: prio: properly report out of memory errors")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Cong Wang <redacted>
---
net/sched/sch_prio.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
From: Cong Wang <hidden> Date: 2016-06-13 16:29:09
On Sun, Jun 12, 2016 at 10:03 PM, Eric Dumazet [off-list ref] wrote:
From: Eric Dumazet <edumazet@google.com>
Now prio_init() can return -ENOMEM, it also has to make sure
any allocated qdisc are freed, since the caller (qdisc_create()) wont
call ->destroy() handler for us.
But prio_tune() is called by ->change() too, so just call prio_destroy()
inside prio_tune() ?
From: Eric Dumazet <hidden> Date: 2016-06-13 17:13:26
On Mon, 2016-06-13 at 09:28 -0700, Cong Wang wrote:
On Sun, Jun 12, 2016 at 10:03 PM, Eric Dumazet [off-list ref] wrote:
quoted
From: Eric Dumazet <edumazet@google.com>
Now prio_init() can return -ENOMEM, it also has to make sure
any allocated qdisc are freed, since the caller (qdisc_create()) wont
call ->destroy() handler for us.
But prio_tune() is called by ->change() too, so just call prio_destroy()
inside prio_tune() ?
Because the following should work :
tc qdisc replace dev eth0 root prio bands 3
(later...)
tc qdisc change dev eth0 root prio bands 5 // memory allocation failure
We must not destroy the qdisc on a change operation if it fails.
Only the init should clean the qdisc state/memory/children since qdisc
wont be created for real.
Presumably we should commit changes on qdisc only if the whole
->change() succeeded, but I guess lot of qdisc are buggy in this
respect.
From: Eric Dumazet <hidden> Date: 2016-06-13 18:33:35
From: Eric Dumazet <edumazet@google.com>
Now prio_init() can return -ENOMEM, it also has to make sure
any allocated qdiscs are freed, since the caller (qdisc_create()) wont
call ->destroy() handler for us.
More generally, we want a transactional behavior for "tc qdisc
change ...", so prio_tune() should not make modifications if
any error is returned.
It means that we must validate parameters and allocate missing qdisc(s)
before taking root qdisc lock exactly once, to not leave the prio qdisc
in an intermediate state.
Fixes: cbdf45116478 ("net_sched: prio: properly report out of memory errors")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Cong Wang <redacted>
---
net/sched/sch_prio.c | 57 ++++++++++++++++++++-------------------------------
1 file changed, 23 insertions(+), 34 deletions(-)
@@ -187,54 +188,42 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt)return-EINVAL;}+/* Before commit, make sure we can allocate all new qdiscs */+for(i=oldbands;i<qopt->bands;i++){+queues[i]=qdisc_create_dflt(sch->dev_queue,&pfifo_qdisc_ops,+TC_H_MAKE(sch->handle,i+1));+if(!queues[i]){+while(i>oldbands)+qdisc_destroy(queues[--i]);+return-ENOMEM;+}+}+sch_tree_lock(sch);q->bands=qopt->bands;memcpy(q->prio2band,qopt->priomap,TC_PRIO_MAX+1);-for(i=q->bands;i<TCQ_PRIO_BANDS;i++){+for(i=q->bands;i<oldbands;i++){structQdisc*child=q->queues[i];-q->queues[i]=&noop_qdisc;-if(child!=&noop_qdisc){-qdisc_tree_reduce_backlog(child,child->q.qlen,child->qstats.backlog);-qdisc_destroy(child);-}-}-sch_tree_unlock(sch);-for(i=0;i<q->bands;i++){-structQdisc*child;+qdisc_tree_reduce_backlog(child,child->q.qlen,+child->qstats.backlog);+qdisc_destroy(child);+}-if(q->queues[i]!=&noop_qdisc)-continue;+for(i=oldbands;i<q->bands;i++)+q->queues[i]=queues[i];-child=qdisc_create_dflt(sch->dev_queue,&pfifo_qdisc_ops,-TC_H_MAKE(sch->handle,i+1));-if(!child)-return-ENOMEM;-sch_tree_lock(sch);-q->queues[i]=child;-sch_tree_unlock(sch);-}+sch_tree_unlock(sch);return0;}staticintprio_init(structQdisc*sch,structnlattr*opt){-structprio_sched_data*q=qdisc_priv(sch);-inti;--for(i=0;i<TCQ_PRIO_BANDS;i++)-q->queues[i]=&noop_qdisc;--if(opt==NULL){+if(!opt)return-EINVAL;-}else{-interr;-if((err=prio_tune(sch,opt))!=0)-returnerr;-}-return0;+returnprio_tune(sch,opt);}staticintprio_dump(structQdisc*sch,structsk_buff*skb)
From: Cong Wang <hidden> Date: 2016-06-13 21:21:37
On Mon, Jun 13, 2016 at 11:33 AM, Eric Dumazet [off-list ref] wrote:
From: Eric Dumazet <edumazet@google.com>
Now prio_init() can return -ENOMEM, it also has to make sure
any allocated qdiscs are freed, since the caller (qdisc_create()) wont
call ->destroy() handler for us.
More generally, we want a transactional behavior for "tc qdisc
change ...", so prio_tune() should not make modifications if
any error is returned.
It means that we must validate parameters and allocate missing qdisc(s)
before taking root qdisc lock exactly once, to not leave the prio qdisc
in an intermediate state.
Fixes: cbdf45116478 ("net_sched: prio: properly report out of memory errors")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Cong Wang <redacted>
Looks good to me,
Acked-by: Cong Wang <redacted>
Thanks!
From: David Miller <davem@davemloft.net> Date: 2016-06-15 19:30:44
From: Eric Dumazet <redacted>
Date: Mon, 13 Jun 2016 11:33:32 -0700
From: Eric Dumazet <edumazet@google.com>
Now prio_init() can return -ENOMEM, it also has to make sure
any allocated qdiscs are freed, since the caller (qdisc_create()) wont
call ->destroy() handler for us.
More generally, we want a transactional behavior for "tc qdisc
change ...", so prio_tune() should not make modifications if
any error is returned.
It means that we must validate parameters and allocate missing qdisc(s)
before taking root qdisc lock exactly once, to not leave the prio qdisc
in an intermediate state.
Fixes: cbdf45116478 ("net_sched: prio: properly report out of memory errors")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Cong Wang <redacted>