Thu, Dec 14, 2017 at 02:05:55AM CET, jakub.kicin...@netronome.com wrote:
>On Wed, 13 Dec 2017 16:10:33 +0100, Jiri Pirko wrote:
>> From: Jiri Pirko <j...@mellanox.com>
>> 
>> During block bind, we need to check tc offload feature. If it is
>> disabled yet still the block contains offloaded filters, forbid the
>> bind. Also forbid to register callback for a block that already
>> containes offloaded filters, as the play back is not supported now.
>> For keeping track of offloaded filters there is a new counter
>> introduced, alongside with couple of helpers called from cls_* code.
>> These helpers set and clear TCA_CLS_FLAGS_IN_HW flag.
>> 
>> Signed-off-by: Jiri Pirko <j...@mellanox.com>
>
>> diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
>> index de9dbcb..ac25142 100644
>> --- a/net/sched/cls_api.c
>> +++ b/net/sched/cls_api.c
>> @@ -266,31 +266,50 @@ void tcf_chain_put(struct tcf_chain *chain)
>>  }
>>  EXPORT_SYMBOL(tcf_chain_put);
>>  
>> -static void tcf_block_offload_cmd(struct tcf_block *block, struct Qdisc *q,
>> +static bool tcf_block_offload_in_use(struct tcf_block *block)
>> +{
>> +    return block->offloadcnt;
>> +}
>> +
>> +static void tcf_block_offload_cmd(struct tcf_block *block,
>> +                              struct net_device *dev,
>>                                struct tcf_block_ext_info *ei,
>>                                enum tc_block_command command)
>>  {
>> -    struct net_device *dev = q->dev_queue->dev;
>>      struct tc_block_offload bo = {};
>>  
>> -    if (!dev->netdev_ops->ndo_setup_tc)
>> -            return;
>>      bo.command = command;
>>      bo.binder_type = ei->binder_type;
>>      bo.block = block;
>>      dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo);
>>  }
>>  
>> -static void tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q,
>> -                               struct tcf_block_ext_info *ei)
>> +static int tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q,
>> +                              struct tcf_block_ext_info *ei)
>>  {
>> -    tcf_block_offload_cmd(block, q, ei, TC_BLOCK_BIND);
>> +    struct net_device *dev = q->dev_queue->dev;
>> +
>> +    if (!dev->netdev_ops->ndo_setup_tc)
>> +            return 0;
>> +
>> +    /* If tc offload feature is disabled and the block we try to bind
>> +     * to already has some offloaded filters, forbid to bind.
>> +     */
>> +    if (!tc_can_offload(dev) && tcf_block_offload_in_use(block))
>> +            return -EOPNOTSUPP;
>
>I don't understand the tc_can_offload(dev) check here.  The flow is -
>on bind if TC offloads are enabled the new port will get a TC_BLOCK_BIND
>and request a register, but the register will fail since the block is
>offloaded?

The point of this check is to disallow dev with tc offload disabled to
share a block which already holds offloaded filters.

That is similar to disallow disabling tc offload on device that shares a
block which contains offloaded filters.



>
>But the whole bind operation does not fail, so user will not see an
>error.  The block will get bound but port's driver has no way to
>register the callback.  I'm sorry if I'm just being slow here..
>
>> +    tcf_block_offload_cmd(block, dev, ei, TC_BLOCK_BIND);
>> +    return 0;

The thing is that driver which does not support TC_BLOCK_BIND would
return -EOPNOTSUPP here. For those drivers we continue, they just won't
have block cb registered so they won't receive cb calls to offload
filters.


>>  }
>>  
>>  static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc 
>> *q,
>>                                   struct tcf_block_ext_info *ei)
>>  {
>> -    tcf_block_offload_cmd(block, q, ei, TC_BLOCK_UNBIND);
>> +    struct net_device *dev = q->dev_queue->dev;
>> +
>> +    if (!dev->netdev_ops->ndo_setup_tc)
>> +            return;
>> +    tcf_block_offload_cmd(block, dev, ei, TC_BLOCK_UNBIND);
>>  }
>>  
>>  static int
>> @@ -499,10 +518,15 @@ int tcf_block_get_ext(struct tcf_block **p_block, 
>> struct Qdisc *q,
>>      if (err)
>>              goto err_chain_head_change_cb_add;
>>  
>> -    tcf_block_offload_bind(block, q, ei);
>> +    err = tcf_block_offload_bind(block, q, ei);
>> +    if (err)
>> +            goto err_block_offload_bind;
>
>Would it perhaps make more sense to add a TC_BLOCK_JOIN or some such?

Why? Just a namechange?


>IIUC the problem is we don't know whether the driver/callee of the new
>port is aware of previous callbacks/filters and we can't replay them.
>
>Obviously registering new callbacks on offloaded blocks is a no-go.
>For simple bind to a new port of an ASIC which already knows the rule
>set, we just need to ask all callbacks if they know the port and as
>long as any of them responds "yes" we are safe to assume the bind is OK.
>
>(Existing drivers will all respond with EOPNOTSUPP to a new unknown command.)
>
>Does that make sense?

Hmm, I understand what you say. I have to think about that a bit more.

Thanks!

>
>>      *p_block = block;
>>      return 0;
>>  
>> +err_block_offload_bind:
>> +    tcf_chain_head_change_cb_del(tcf_block_chain_zero(block), ei);
>>  err_chain_head_change_cb_add:
>>      tcf_block_owner_del(block, q, ei->binder_type);
>>  err_block_owner_add:
>> @@ -630,9 +654,16 @@ struct tcf_block_cb *__tcf_block_cb_register(struct 
>> tcf_block *block,
>>  {
>>      struct tcf_block_cb *block_cb;
>>  
>> +    /* At this point, playback of previous block cb calls is not supported,
>> +     * so forbid to register to block which already has some offloaded
>> +     * filters present.
>> +     */
>> +    if (tcf_block_offload_in_use(block))
>> +            return ERR_PTR(-EOPNOTSUPP);
>> 
>>      block_cb = kzalloc(sizeof(*block_cb), GFP_KERNEL);
>>      if (!block_cb)
>> -            return NULL;
>> +            return ERR_PTR(-ENOMEM);
>>      block_cb->cb = cb;
>>      block_cb->cb_ident = cb_ident;
>>      block_cb->cb_priv = cb_priv;
>> @@ -648,7 +679,7 @@ int tcf_block_cb_register(struct tcf_block *block,
>>      struct tcf_block_cb *block_cb;
>>  
>>      block_cb = __tcf_block_cb_register(block, cb, cb_ident, cb_priv);
>> -    return block_cb ? 0 : -ENOMEM;
>> +    return IS_ERR(block_cb) ? PTR_ERR(block_cb) : 0;
>>  }
>>  EXPORT_SYMBOL(tcf_block_cb_register);
>>  

Reply via email to