Re: [PATCH net-next v2 05/10] net: sched: use Qdisc rcu API instead of relying on rtnl lock

2018-09-21 Thread Cong Wang
On Thu, Sep 20, 2018 at 12:21 AM Vlad Buslov  wrote:
>
>
> On Wed 19 Sep 2018 at 22:04, Cong Wang  wrote:
> > On Mon, Sep 17, 2018 at 12:19 AM Vlad Buslov  wrote:
> >> +static void tcf_qdisc_put(struct Qdisc *q, bool rtnl_held)
> >> +{
> >> +   if (!q)
> >> +   return;
> >> +
> >> +   if (rtnl_held)
> >> +   qdisc_put(q);
> >> +   else
> >> +   qdisc_put_unlocked(q);
> >> +}
> >
> > This is very ugly. You should know whether RTNL is held or
> > not when calling it.
> >
> > What's more, all of your code passes true, so why do you
> > need a parameter for rtnl_held?
>
> It passes true because currently rule update handlers still registered
> as locked. This is a preparation for next patch set where this would be
> changed to proper variable that depends on qdics and classifier type.

You can always add it when you really need it.

I doubt you need such a tiny wrapper even in the next patchset,
as it can be easily folded into callers.


Re: [PATCH net-next v2 05/10] net: sched: use Qdisc rcu API instead of relying on rtnl lock

2018-09-20 Thread Vlad Buslov


On Wed 19 Sep 2018 at 22:04, Cong Wang  wrote:
> On Mon, Sep 17, 2018 at 12:19 AM Vlad Buslov  wrote:
>> +static void tcf_qdisc_put(struct Qdisc *q, bool rtnl_held)
>> +{
>> +   if (!q)
>> +   return;
>> +
>> +   if (rtnl_held)
>> +   qdisc_put(q);
>> +   else
>> +   qdisc_put_unlocked(q);
>> +}
>
> This is very ugly. You should know whether RTNL is held or
> not when calling it.
>
> What's more, all of your code passes true, so why do you
> need a parameter for rtnl_held?

It passes true because currently rule update handlers still registered
as locked. This is a preparation for next patch set where this would be
changed to proper variable that depends on qdics and classifier type.


Re: [PATCH net-next v2 05/10] net: sched: use Qdisc rcu API instead of relying on rtnl lock

2018-09-19 Thread Cong Wang
On Mon, Sep 17, 2018 at 12:19 AM Vlad Buslov  wrote:
> +static void tcf_qdisc_put(struct Qdisc *q, bool rtnl_held)
> +{
> +   if (!q)
> +   return;
> +
> +   if (rtnl_held)
> +   qdisc_put(q);
> +   else
> +   qdisc_put_unlocked(q);
> +}

This is very ugly. You should know whether RTNL is held or
not when calling it.

What's more, all of your code passes true, so why do you
need a parameter for rtnl_held?


[PATCH net-next v2 05/10] net: sched: use Qdisc rcu API instead of relying on rtnl lock

2018-09-17 Thread Vlad Buslov
As a preparation from removing rtnl lock dependency from rules update path,
use Qdisc rcu and reference counting capabilities instead of relying on
rtnl lock while working with Qdiscs. Create new tcf_block_release()
function, and use it to free resources taken by tcf_block_find().
Currently, this function only releases Qdisc and it is extended in next
patches in this series.

Signed-off-by: Vlad Buslov 
Acked-by: Jiri Pirko 
---
 net/sched/cls_api.c | 88 -
 1 file changed, 73 insertions(+), 15 deletions(-)

diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 1a67af8a6e8c..cfa4a02a6a1a 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -527,6 +527,17 @@ static struct tcf_block *tcf_block_lookup(struct net *net, 
u32 block_index)
return idr_find(&tn->idr, block_index);
 }
 
+static void tcf_qdisc_put(struct Qdisc *q, bool rtnl_held)
+{
+   if (!q)
+   return;
+
+   if (rtnl_held)
+   qdisc_put(q);
+   else
+   qdisc_put_unlocked(q);
+}
+
 /* Find tcf block.
  * Set q, parent, cl when appropriate.
  */
@@ -537,6 +548,7 @@ static struct tcf_block *tcf_block_find(struct net *net, 
struct Qdisc **q,
struct netlink_ext_ack *extack)
 {
struct tcf_block *block;
+   int err = 0;
 
if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
block = tcf_block_lookup(net, block_index);
@@ -548,55 +560,91 @@ static struct tcf_block *tcf_block_find(struct net *net, 
struct Qdisc **q,
const struct Qdisc_class_ops *cops;
struct net_device *dev;
 
+   rcu_read_lock();
+
/* Find link */
-   dev = __dev_get_by_index(net, ifindex);
-   if (!dev)
+   dev = dev_get_by_index_rcu(net, ifindex);
+   if (!dev) {
+   rcu_read_unlock();
return ERR_PTR(-ENODEV);
+   }
 
/* Find qdisc */
if (!*parent) {
*q = dev->qdisc;
*parent = (*q)->handle;
} else {
-   *q = qdisc_lookup(dev, TC_H_MAJ(*parent));
+   *q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent));
if (!*q) {
NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't 
exists");
-   return ERR_PTR(-EINVAL);
+   err = -EINVAL;
+   goto errout_rcu;
}
}
 
+   *q = qdisc_refcount_inc_nz(*q);
+   if (!*q) {
+   NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists");
+   err = -EINVAL;
+   goto errout_rcu;
+   }
+
/* Is it classful? */
cops = (*q)->ops->cl_ops;
if (!cops) {
NL_SET_ERR_MSG(extack, "Qdisc not classful");
-   return ERR_PTR(-EINVAL);
+   err = -EINVAL;
+   goto errout_rcu;
}
 
if (!cops->tcf_block) {
NL_SET_ERR_MSG(extack, "Class doesn't support blocks");
-   return ERR_PTR(-EOPNOTSUPP);
+   err = -EOPNOTSUPP;
+   goto errout_rcu;
}
 
+   /* At this point we know that qdisc is not noop_qdisc,
+* which means that qdisc holds a reference to net_device
+* and we hold a reference to qdisc, so it is safe to release
+* rcu read lock.
+*/
+   rcu_read_unlock();
+
/* Do we search for filter, attached to class? */
if (TC_H_MIN(*parent)) {
*cl = cops->find(*q, *parent);
if (*cl == 0) {
NL_SET_ERR_MSG(extack, "Specified class doesn't 
exist");
-   return ERR_PTR(-ENOENT);
+   err = -ENOENT;
+   goto errout_qdisc;
}
}
 
/* And the last stroke */
block = cops->tcf_block(*q, *cl, extack);
-   if (!block)
-   return ERR_PTR(-EINVAL);
+   if (!block) {
+   err = -EINVAL;
+   goto errout_qdisc;
+   }
if (tcf_block_shared(block)) {
NL_SET_ERR_MSG(extack, "This filter block is shared. 
Please use the block index to manipulate the filters");
-   return ERR_PTR(-EOPNOTSUPP);
+   err = -EOPNOTSUPP;
+   goto errout_qdisc;
}
}
 
return blo