Skip to content

Commit

Permalink
net: sched: introduce shared filter blocks infrastructure
Browse files Browse the repository at this point in the history
Allow qdiscs to share filter blocks among them. Each qdisc type has to
use block get/put extended modifications that enable sharing.
Shared blocks are tracked within each net namespace and identified
by u32 index. This index is passed from user during the qdisc creation.
If user passes index that is not used by any other qdisc, new block
is created. If user passes index that is already used, the existing
block will be re-used.

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>
  • Loading branch information
Jiri Pirko authored and David S. Miller committed Jan 17, 2018
1 parent a9b1944 commit 4861738
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 24 deletions.
7 changes: 7 additions & 0 deletions include/net/pkt_cls.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct tcf_block_ext_info {
enum tcf_block_binder_type binder_type;
tcf_chain_head_change_t *chain_head_change;
void *chain_head_change_priv;
u32 block_index;
};

struct tcf_block_cb;
Expand All @@ -48,8 +49,14 @@ void tcf_block_put(struct tcf_block *block);
void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,
struct tcf_block_ext_info *ei);

static inline bool tcf_block_shared(struct tcf_block *block)
{
return block->index;
}

static inline struct Qdisc *tcf_block_q(struct tcf_block *block)
{
WARN_ON(tcf_block_shared(block));
return block->q;
}

Expand Down
2 changes: 2 additions & 0 deletions include/net/sch_generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,8 @@ struct tcf_chain {

struct tcf_block {
struct list_head chain_list;
u32 index; /* block index for shared blocks */
unsigned int refcnt;
struct net *net;
struct Qdisc *q;
struct list_head cb_list;
Expand Down
167 changes: 143 additions & 24 deletions net/sched/cls_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/slab.h>
#include <linux/idr.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/netlink.h>
Expand Down Expand Up @@ -333,22 +334,44 @@ tcf_chain_head_change_cb_del(struct tcf_chain *chain,
WARN_ON(1);
}

static struct tcf_chain *tcf_block_chain_zero(struct tcf_block *block)
struct tcf_net {
struct idr idr;
};

static unsigned int tcf_net_id;

static int tcf_block_insert(struct tcf_block *block, struct net *net,
u32 block_index, struct netlink_ext_ack *extack)
{
return list_first_entry(&block->chain_list, struct tcf_chain, list);
struct tcf_net *tn = net_generic(net, tcf_net_id);
int err;

err = idr_alloc_ext(&tn->idr, block, NULL, block_index,
block_index + 1, GFP_KERNEL);
if (err)
return err;
block->index = block_index;
return 0;
}

int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q,
struct tcf_block_ext_info *ei,
struct netlink_ext_ack *extack)
static void tcf_block_remove(struct tcf_block *block, struct net *net)
{
struct tcf_net *tn = net_generic(net, tcf_net_id);

idr_remove_ext(&tn->idr, block->index);
}

static struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q,
struct netlink_ext_ack *extack)
{
struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL);
struct tcf_block *block;
struct tcf_chain *chain;
int err;

block = kzalloc(sizeof(*block), GFP_KERNEL);
if (!block) {
NL_SET_ERR_MSG(extack, "Memory allocation for block failed");
return -ENOMEM;
return ERR_PTR(-ENOMEM);
}
INIT_LIST_HEAD(&block->chain_list);
INIT_LIST_HEAD(&block->cb_list);
Expand All @@ -360,20 +383,76 @@ int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q,
err = -ENOMEM;
goto err_chain_create;
}
block->net = qdisc_net(q);
block->refcnt = 1;
block->net = net;
block->q = q;
return block;

err_chain_create:
kfree(block);
return ERR_PTR(err);
}

static struct tcf_block *tcf_block_lookup(struct net *net, u32 block_index)
{
struct tcf_net *tn = net_generic(net, tcf_net_id);

return idr_find_ext(&tn->idr, block_index);
}

static struct tcf_chain *tcf_block_chain_zero(struct tcf_block *block)
{
return list_first_entry(&block->chain_list, struct tcf_chain, list);
}

int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q,
struct tcf_block_ext_info *ei,
struct netlink_ext_ack *extack)
{
struct net *net = qdisc_net(q);
struct tcf_block *block = NULL;
bool created = false;
int err;

if (ei->block_index) {
/* block_index not 0 means the shared block is requested */
block = tcf_block_lookup(net, ei->block_index);
if (block)
block->refcnt++;
}

if (!block) {
block = tcf_block_create(net, q, extack);
if (IS_ERR(block))
return PTR_ERR(block);
created = true;
if (ei->block_index) {
err = tcf_block_insert(block, net,
ei->block_index, extack);
if (err)
goto err_block_insert;
}
}

err = tcf_chain_head_change_cb_add(tcf_block_chain_zero(block),
ei, extack);
if (err)
goto err_chain_head_change_cb_add;
block->net = qdisc_net(q);
block->q = q;
tcf_block_offload_bind(block, q, ei);
*p_block = block;
return 0;

err_chain_create:
kfree(block);
err_chain_head_change_cb_add:
kfree(chain);
if (created) {
if (tcf_block_shared(block))
tcf_block_remove(block, net);
err_block_insert:
kfree(tcf_block_chain_zero(block));
kfree(block);
} else {
block->refcnt--;
}
return err;
}
EXPORT_SYMBOL(tcf_block_get_ext);
Expand Down Expand Up @@ -407,26 +486,34 @@ void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,
{
struct tcf_chain *chain, *tmp;

/* Hold a refcnt for all chains, so that they don't disappear
* while we are iterating.
*/
if (!block)
return;
tcf_chain_head_change_cb_del(tcf_block_chain_zero(block), ei);
list_for_each_entry(chain, &block->chain_list, list)
tcf_chain_hold(chain);

list_for_each_entry(chain, &block->chain_list, list)
tcf_chain_flush(chain);
if (--block->refcnt == 0) {
if (tcf_block_shared(block))
tcf_block_remove(block, block->net);

/* Hold a refcnt for all chains, so that they don't disappear
* while we are iterating.
*/
list_for_each_entry(chain, &block->chain_list, list)
tcf_chain_hold(chain);

list_for_each_entry(chain, &block->chain_list, list)
tcf_chain_flush(chain);
}

tcf_block_offload_unbind(block, q, ei);

/* At this point, all the chains should have refcnt >= 1. */
list_for_each_entry_safe(chain, tmp, &block->chain_list, list)
tcf_chain_put(chain);
if (block->refcnt == 0) {
/* At this point, all the chains should have refcnt >= 1. */
list_for_each_entry_safe(chain, tmp, &block->chain_list, list)
tcf_chain_put(chain);

/* Finally, put chain 0 and allow block to be freed. */
tcf_chain_put(tcf_block_chain_zero(block));
/* Finally, put chain 0 and allow block to be freed. */
tcf_chain_put(tcf_block_chain_zero(block));
}
}
EXPORT_SYMBOL(tcf_block_put_ext);

Expand Down Expand Up @@ -1313,18 +1400,50 @@ int tc_setup_cb_call(struct tcf_block *block, struct tcf_exts *exts,
}
EXPORT_SYMBOL(tc_setup_cb_call);

static __net_init int tcf_net_init(struct net *net)
{
struct tcf_net *tn = net_generic(net, tcf_net_id);

idr_init(&tn->idr);
return 0;
}

static void __net_exit tcf_net_exit(struct net *net)
{
struct tcf_net *tn = net_generic(net, tcf_net_id);

idr_destroy(&tn->idr);
}

static struct pernet_operations tcf_net_ops = {
.init = tcf_net_init,
.exit = tcf_net_exit,
.id = &tcf_net_id,
.size = sizeof(struct tcf_net),
};

static int __init tc_filter_init(void)
{
int err;

tc_filter_wq = alloc_ordered_workqueue("tc_filter_workqueue", 0);
if (!tc_filter_wq)
return -ENOMEM;

err = register_pernet_subsys(&tcf_net_ops);
if (err)
goto err_register_pernet_subsys;

rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL, 0);
rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL, 0);
rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter,
tc_dump_tfilter, 0);

return 0;

err_register_pernet_subsys:
destroy_workqueue(tc_filter_wq);
return err;
}

subsys_initcall(tc_filter_init);

0 comments on commit 4861738

Please sign in to comment.