Skip to content

Commit caa7260

Browse files
jpirkodavem330
authored andcommitted
net: sched: keep track of offloaded filters and check tc offload feature
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 contains 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 <jiri@mellanox.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Acked-by: David Ahern <dsahern@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent edf6711 commit caa7260

File tree

6 files changed

+99
-23
lines changed

6 files changed

+99
-23
lines changed

include/net/sch_generic.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,26 @@ struct tcf_block {
289289
struct list_head cb_list;
290290
struct list_head owner_list;
291291
bool keep_dst;
292+
unsigned int offloadcnt; /* Number of oddloaded filters */
293+
unsigned int nooffloaddevcnt; /* Number of devs unable to do offload */
292294
};
293295

296+
static inline void tcf_block_offload_inc(struct tcf_block *block, u32 *flags)
297+
{
298+
if (*flags & TCA_CLS_FLAGS_IN_HW)
299+
return;
300+
*flags |= TCA_CLS_FLAGS_IN_HW;
301+
block->offloadcnt++;
302+
}
303+
304+
static inline void tcf_block_offload_dec(struct tcf_block *block, u32 *flags)
305+
{
306+
if (!(*flags & TCA_CLS_FLAGS_IN_HW))
307+
return;
308+
*flags &= ~TCA_CLS_FLAGS_IN_HW;
309+
block->offloadcnt--;
310+
}
311+
294312
static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz)
295313
{
296314
struct qdisc_skb_cb *qcb;

net/sched/cls_api.c

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -265,31 +265,66 @@ void tcf_chain_put(struct tcf_chain *chain)
265265
}
266266
EXPORT_SYMBOL(tcf_chain_put);
267267

268-
static void tcf_block_offload_cmd(struct tcf_block *block, struct Qdisc *q,
269-
struct tcf_block_ext_info *ei,
270-
enum tc_block_command command)
268+
static bool tcf_block_offload_in_use(struct tcf_block *block)
269+
{
270+
return block->offloadcnt;
271+
}
272+
273+
static int tcf_block_offload_cmd(struct tcf_block *block,
274+
struct net_device *dev,
275+
struct tcf_block_ext_info *ei,
276+
enum tc_block_command command)
271277
{
272-
struct net_device *dev = q->dev_queue->dev;
273278
struct tc_block_offload bo = {};
274279

275-
if (!dev->netdev_ops->ndo_setup_tc)
276-
return;
277280
bo.command = command;
278281
bo.binder_type = ei->binder_type;
279282
bo.block = block;
280-
dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo);
283+
return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo);
281284
}
282285

283-
static void tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q,
284-
struct tcf_block_ext_info *ei)
286+
static int tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q,
287+
struct tcf_block_ext_info *ei)
285288
{
286-
tcf_block_offload_cmd(block, q, ei, TC_BLOCK_BIND);
289+
struct net_device *dev = q->dev_queue->dev;
290+
int err;
291+
292+
if (!dev->netdev_ops->ndo_setup_tc)
293+
goto no_offload_dev_inc;
294+
295+
/* If tc offload feature is disabled and the block we try to bind
296+
* to already has some offloaded filters, forbid to bind.
297+
*/
298+
if (!tc_can_offload(dev) && tcf_block_offload_in_use(block))
299+
return -EOPNOTSUPP;
300+
301+
err = tcf_block_offload_cmd(block, dev, ei, TC_BLOCK_BIND);
302+
if (err == -EOPNOTSUPP)
303+
goto no_offload_dev_inc;
304+
return err;
305+
306+
no_offload_dev_inc:
307+
if (tcf_block_offload_in_use(block))
308+
return -EOPNOTSUPP;
309+
block->nooffloaddevcnt++;
310+
return 0;
287311
}
288312

289313
static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q,
290314
struct tcf_block_ext_info *ei)
291315
{
292-
tcf_block_offload_cmd(block, q, ei, TC_BLOCK_UNBIND);
316+
struct net_device *dev = q->dev_queue->dev;
317+
int err;
318+
319+
if (!dev->netdev_ops->ndo_setup_tc)
320+
goto no_offload_dev_dec;
321+
err = tcf_block_offload_cmd(block, dev, ei, TC_BLOCK_UNBIND);
322+
if (err == -EOPNOTSUPP)
323+
goto no_offload_dev_dec;
324+
return;
325+
326+
no_offload_dev_dec:
327+
WARN_ON(block->nooffloaddevcnt-- == 0);
293328
}
294329

295330
static int
@@ -502,10 +537,16 @@ int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q,
502537
ei, extack);
503538
if (err)
504539
goto err_chain_head_change_cb_add;
505-
tcf_block_offload_bind(block, q, ei);
540+
541+
err = tcf_block_offload_bind(block, q, ei);
542+
if (err)
543+
goto err_block_offload_bind;
544+
506545
*p_block = block;
507546
return 0;
508547

548+
err_block_offload_bind:
549+
tcf_chain_head_change_cb_del(tcf_block_chain_zero(block), ei);
509550
err_chain_head_change_cb_add:
510551
tcf_block_owner_del(block, q, ei->binder_type);
511552
err_block_owner_add:
@@ -637,9 +678,16 @@ struct tcf_block_cb *__tcf_block_cb_register(struct tcf_block *block,
637678
{
638679
struct tcf_block_cb *block_cb;
639680

681+
/* At this point, playback of previous block cb calls is not supported,
682+
* so forbid to register to block which already has some offloaded
683+
* filters present.
684+
*/
685+
if (tcf_block_offload_in_use(block))
686+
return ERR_PTR(-EOPNOTSUPP);
687+
640688
block_cb = kzalloc(sizeof(*block_cb), GFP_KERNEL);
641689
if (!block_cb)
642-
return NULL;
690+
return ERR_PTR(-ENOMEM);
643691
block_cb->cb = cb;
644692
block_cb->cb_ident = cb_ident;
645693
block_cb->cb_priv = cb_priv;
@@ -655,7 +703,7 @@ int tcf_block_cb_register(struct tcf_block *block,
655703
struct tcf_block_cb *block_cb;
656704

657705
block_cb = __tcf_block_cb_register(block, cb, cb_ident, cb_priv);
658-
return block_cb ? 0 : -ENOMEM;
706+
return IS_ERR(block_cb) ? PTR_ERR(block_cb) : 0;
659707
}
660708
EXPORT_SYMBOL(tcf_block_cb_register);
661709

@@ -685,6 +733,10 @@ static int tcf_block_cb_call(struct tcf_block *block, enum tc_setup_type type,
685733
int ok_count = 0;
686734
int err;
687735

736+
/* Make sure all netdevs sharing this block are offload-capable. */
737+
if (block->nooffloaddevcnt && err_stop)
738+
return -EOPNOTSUPP;
739+
688740
list_for_each_entry(block_cb, &block->cb_list, list) {
689741
err = block_cb->cb(type, type_data, block_cb->cb_priv);
690742
if (err) {

net/sched/cls_bpf.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,16 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog,
167167
cls_bpf.exts_integrated = obj->exts_integrated;
168168
cls_bpf.gen_flags = obj->gen_flags;
169169

170+
if (oldprog)
171+
tcf_block_offload_dec(block, &oldprog->gen_flags);
172+
170173
err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSBPF, &cls_bpf, skip_sw);
171174
if (prog) {
172175
if (err < 0) {
173176
cls_bpf_offload_cmd(tp, oldprog, prog);
174177
return err;
175178
} else if (err > 0) {
176-
prog->gen_flags |= TCA_CLS_FLAGS_IN_HW;
179+
tcf_block_offload_inc(block, &prog->gen_flags);
177180
}
178181
}
179182

net/sched/cls_flower.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f)
229229

230230
tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER,
231231
&cls_flower, false);
232+
tcf_block_offload_dec(block, &f->flags);
232233
}
233234

234235
static int fl_hw_replace_filter(struct tcf_proto *tp,
@@ -256,7 +257,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
256257
fl_hw_destroy_filter(tp, f);
257258
return err;
258259
} else if (err > 0) {
259-
f->flags |= TCA_CLS_FLAGS_IN_HW;
260+
tcf_block_offload_inc(block, &f->flags);
260261
}
261262

262263
if (skip_sw && !(f->flags & TCA_CLS_FLAGS_IN_HW))

net/sched/cls_matchall.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp,
8181
cls_mall.cookie = cookie;
8282

8383
tc_setup_cb_call(block, NULL, TC_SETUP_CLSMATCHALL, &cls_mall, false);
84+
tcf_block_offload_dec(block, &head->flags);
8485
}
8586

8687
static int mall_replace_hw_filter(struct tcf_proto *tp,
@@ -103,7 +104,7 @@ static int mall_replace_hw_filter(struct tcf_proto *tp,
103104
mall_destroy_hw_filter(tp, head, cookie);
104105
return err;
105106
} else if (err > 0) {
106-
head->flags |= TCA_CLS_FLAGS_IN_HW;
107+
tcf_block_offload_inc(block, &head->flags);
107108
}
108109

109110
if (skip_sw && !(head->flags & TCA_CLS_FLAGS_IN_HW))

net/sched/cls_u32.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -529,16 +529,17 @@ static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h,
529529
return 0;
530530
}
531531

532-
static void u32_remove_hw_knode(struct tcf_proto *tp, u32 handle)
532+
static void u32_remove_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n)
533533
{
534534
struct tcf_block *block = tp->chain->block;
535535
struct tc_cls_u32_offload cls_u32 = {};
536536

537537
tc_cls_common_offload_init(&cls_u32.common, tp);
538538
cls_u32.command = TC_CLSU32_DELETE_KNODE;
539-
cls_u32.knode.handle = handle;
539+
cls_u32.knode.handle = n->handle;
540540

541541
tc_setup_cb_call(block, NULL, TC_SETUP_CLSU32, &cls_u32, false);
542+
tcf_block_offload_dec(block, &n->flags);
542543
}
543544

544545
static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
@@ -567,10 +568,10 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
567568

568569
err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSU32, &cls_u32, skip_sw);
569570
if (err < 0) {
570-
u32_remove_hw_knode(tp, n->handle);
571+
u32_remove_hw_knode(tp, n);
571572
return err;
572573
} else if (err > 0) {
573-
n->flags |= TCA_CLS_FLAGS_IN_HW;
574+
tcf_block_offload_inc(block, &n->flags);
574575
}
575576

576577
if (skip_sw && !(n->flags & TCA_CLS_FLAGS_IN_HW))
@@ -589,7 +590,7 @@ static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
589590
RCU_INIT_POINTER(ht->ht[h],
590591
rtnl_dereference(n->next));
591592
tcf_unbind_filter(tp, &n->res);
592-
u32_remove_hw_knode(tp, n->handle);
593+
u32_remove_hw_knode(tp, n);
593594
idr_remove_ext(&ht->handle_idr, n->handle);
594595
if (tcf_exts_get_net(&n->exts))
595596
call_rcu(&n->rcu, u32_delete_key_freepf_rcu);
@@ -682,7 +683,7 @@ static int u32_delete(struct tcf_proto *tp, void *arg, bool *last)
682683
goto out;
683684

684685
if (TC_U32_KEY(ht->handle)) {
685-
u32_remove_hw_knode(tp, ht->handle);
686+
u32_remove_hw_knode(tp, (struct tc_u_knode *)ht);
686687
ret = u32_delete_key(tp, (struct tc_u_knode *)ht);
687688
goto out;
688689
}

0 commit comments

Comments
 (0)