Commit 2190d1d0 authored by Jiri Pirko's avatar Jiri Pirko Committed by David S. Miller

net: sched: introduce helpers to work with filter chains

Introduce struct tcf_chain object and set of helpers around it. Wraps up
insertion, deletion and search in the filter chain.
Signed-off-by: default avatarJiri Pirko <jiri@mellanox.com>
Acked-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7961973a
...@@ -248,10 +248,15 @@ struct qdisc_skb_cb { ...@@ -248,10 +248,15 @@ struct qdisc_skb_cb {
unsigned char data[QDISC_CB_PRIV_LEN]; unsigned char data[QDISC_CB_PRIV_LEN];
}; };
struct tcf_block { struct tcf_chain {
struct tcf_proto __rcu *filter_chain;
struct tcf_proto __rcu **p_filter_chain; struct tcf_proto __rcu **p_filter_chain;
}; };
struct tcf_block {
struct tcf_chain *chain;
};
static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz) static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz)
{ {
struct qdisc_skb_cb *qcb; struct qdisc_skb_cb *qcb;
......
...@@ -106,13 +106,12 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb, ...@@ -106,13 +106,12 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
struct nlmsghdr *n, struct nlmsghdr *n,
struct tcf_proto __rcu **chain, int event) struct tcf_chain *chain, int event)
{ {
struct tcf_proto __rcu **it_chain;
struct tcf_proto *tp; struct tcf_proto *tp;
for (it_chain = chain; (tp = rtnl_dereference(*it_chain)) != NULL; for (tp = rtnl_dereference(chain->filter_chain);
it_chain = &tp->next) tp; tp = rtnl_dereference(tp->next))
tfilter_notify(net, oskb, n, tp, 0, event, false); tfilter_notify(net, oskb, n, tp, 0, event, false);
} }
...@@ -187,26 +186,49 @@ static void tcf_proto_destroy(struct tcf_proto *tp) ...@@ -187,26 +186,49 @@ static void tcf_proto_destroy(struct tcf_proto *tp)
kfree_rcu(tp, rcu); kfree_rcu(tp, rcu);
} }
static void tcf_chain_destroy(struct tcf_proto __rcu **fl) static struct tcf_chain *tcf_chain_create(void)
{
return kzalloc(sizeof(struct tcf_chain), GFP_KERNEL);
}
static void tcf_chain_destroy(struct tcf_chain *chain)
{ {
struct tcf_proto *tp; struct tcf_proto *tp;
while ((tp = rtnl_dereference(*fl)) != NULL) { while ((tp = rtnl_dereference(chain->filter_chain)) != NULL) {
RCU_INIT_POINTER(*fl, tp->next); RCU_INIT_POINTER(chain->filter_chain, tp->next);
tcf_proto_destroy(tp); tcf_proto_destroy(tp);
} }
kfree(chain);
}
static void
tcf_chain_filter_chain_ptr_set(struct tcf_chain *chain,
struct tcf_proto __rcu **p_filter_chain)
{
chain->p_filter_chain = p_filter_chain;
} }
int tcf_block_get(struct tcf_block **p_block, int tcf_block_get(struct tcf_block **p_block,
struct tcf_proto __rcu **p_filter_chain) struct tcf_proto __rcu **p_filter_chain)
{ {
struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL); struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL);
int err;
if (!block) if (!block)
return -ENOMEM; return -ENOMEM;
block->p_filter_chain = p_filter_chain; block->chain = tcf_chain_create();
if (!block->chain) {
err = -ENOMEM;
goto err_chain_create;
}
tcf_chain_filter_chain_ptr_set(block->chain, p_filter_chain);
*p_block = block; *p_block = block;
return 0; return 0;
err_chain_create:
kfree(block);
return err;
} }
EXPORT_SYMBOL(tcf_block_get); EXPORT_SYMBOL(tcf_block_get);
...@@ -214,7 +236,7 @@ void tcf_block_put(struct tcf_block *block) ...@@ -214,7 +236,7 @@ void tcf_block_put(struct tcf_block *block)
{ {
if (!block) if (!block)
return; return;
tcf_chain_destroy(block->p_filter_chain); tcf_chain_destroy(block->chain);
kfree(block); kfree(block);
} }
EXPORT_SYMBOL(tcf_block_put); EXPORT_SYMBOL(tcf_block_put);
...@@ -267,6 +289,65 @@ int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, ...@@ -267,6 +289,65 @@ int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
} }
EXPORT_SYMBOL(tcf_classify); EXPORT_SYMBOL(tcf_classify);
struct tcf_chain_info {
struct tcf_proto __rcu **pprev;
struct tcf_proto __rcu *next;
};
static struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain_info *chain_info)
{
return rtnl_dereference(*chain_info->pprev);
}
static void tcf_chain_tp_insert(struct tcf_chain *chain,
struct tcf_chain_info *chain_info,
struct tcf_proto *tp)
{
if (chain->p_filter_chain &&
*chain_info->pprev == chain->filter_chain)
*chain->p_filter_chain = tp;
RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain_info));
rcu_assign_pointer(*chain_info->pprev, tp);
}
static void tcf_chain_tp_remove(struct tcf_chain *chain,
struct tcf_chain_info *chain_info,
struct tcf_proto *tp)
{
struct tcf_proto *next = rtnl_dereference(chain_info->next);
if (chain->p_filter_chain && tp == chain->filter_chain)
*chain->p_filter_chain = next;
RCU_INIT_POINTER(*chain_info->pprev, next);
}
static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
struct tcf_chain_info *chain_info,
u32 protocol, u32 prio,
bool prio_allocate)
{
struct tcf_proto **pprev;
struct tcf_proto *tp;
/* Check the chain for existence of proto-tcf with this priority */
for (pprev = &chain->filter_chain;
(tp = rtnl_dereference(*pprev)); pprev = &tp->next) {
if (tp->prio >= prio) {
if (tp->prio == prio) {
if (prio_allocate ||
(tp->protocol != protocol && protocol))
return ERR_PTR(-EINVAL);
} else {
tp = NULL;
}
break;
}
}
chain_info->pprev = pprev;
chain_info->next = tp ? tp->next : NULL;
return tp;
}
/* Add/change/delete/get a filter node */ /* Add/change/delete/get a filter node */
static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
...@@ -281,10 +362,9 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -281,10 +362,9 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
u32 parent; u32 parent;
struct net_device *dev; struct net_device *dev;
struct Qdisc *q; struct Qdisc *q;
struct tcf_proto __rcu **back; struct tcf_chain_info chain_info;
struct tcf_proto __rcu **chain; struct tcf_chain *chain;
struct tcf_block *block; struct tcf_block *block;
struct tcf_proto *next;
struct tcf_proto *tp; struct tcf_proto *tp;
const struct Qdisc_class_ops *cops; const struct Qdisc_class_ops *cops;
unsigned long cl; unsigned long cl;
...@@ -369,7 +449,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -369,7 +449,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
err = -EINVAL; err = -EINVAL;
goto errout; goto errout;
} }
chain = block->p_filter_chain; chain = block->chain;
if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) { if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) {
tfilter_notify_chain(net, skb, n, chain, RTM_DELTFILTER); tfilter_notify_chain(net, skb, n, chain, RTM_DELTFILTER);
...@@ -378,22 +458,11 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -378,22 +458,11 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
goto errout; goto errout;
} }
/* Check the chain for existence of proto-tcf with this priority */ tp = tcf_chain_tp_find(chain, &chain_info, protocol,
for (back = chain; prio, prio_allocate);
(tp = rtnl_dereference(*back)) != NULL; if (IS_ERR(tp)) {
back = &tp->next) { err = PTR_ERR(tp);
if (tp->prio >= prio) { goto errout;
if (tp->prio == prio) {
if (prio_allocate ||
(tp->protocol != protocol && protocol)) {
err = -EINVAL;
goto errout;
}
} else {
tp = NULL;
}
break;
}
} }
if (tp == NULL) { if (tp == NULL) {
...@@ -411,7 +480,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -411,7 +480,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
} }
if (prio_allocate) if (prio_allocate)
prio = tcf_auto_prio(rtnl_dereference(*back)); prio = tcf_auto_prio(tcf_chain_tp_prev(&chain_info));
tp = tcf_proto_create(nla_data(tca[TCA_KIND]), tp = tcf_proto_create(nla_data(tca[TCA_KIND]),
protocol, prio, parent, q, block); protocol, prio, parent, q, block);
...@@ -429,8 +498,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -429,8 +498,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
if (fh == 0) { if (fh == 0) {
if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) { if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
next = rtnl_dereference(tp->next); tcf_chain_tp_remove(chain, &chain_info, tp);
RCU_INIT_POINTER(*back, next);
tfilter_notify(net, skb, n, tp, fh, tfilter_notify(net, skb, n, tp, fh,
RTM_DELTFILTER, false); RTM_DELTFILTER, false);
tcf_proto_destroy(tp); tcf_proto_destroy(tp);
...@@ -459,11 +527,10 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -459,11 +527,10 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
err = tp->ops->delete(tp, fh, &last); err = tp->ops->delete(tp, fh, &last);
if (err) if (err)
goto errout; goto errout;
next = rtnl_dereference(tp->next);
tfilter_notify(net, skb, n, tp, t->tcm_handle, tfilter_notify(net, skb, n, tp, t->tcm_handle,
RTM_DELTFILTER, false); RTM_DELTFILTER, false);
if (last) { if (last) {
RCU_INIT_POINTER(*back, next); tcf_chain_tp_remove(chain, &chain_info, tp);
tcf_proto_destroy(tp); tcf_proto_destroy(tp);
} }
goto errout; goto errout;
...@@ -480,10 +547,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -480,10 +547,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh, err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE); n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE);
if (err == 0) { if (err == 0) {
if (tp_created) { if (tp_created)
RCU_INIT_POINTER(tp->next, rtnl_dereference(*back)); tcf_chain_tp_insert(chain, &chain_info, tp);
rcu_assign_pointer(*back, tp);
}
tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER, false); tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER, false);
} else { } else {
if (tp_created) if (tp_created)
...@@ -584,7 +649,8 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -584,7 +649,8 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
struct net_device *dev; struct net_device *dev;
struct Qdisc *q; struct Qdisc *q;
struct tcf_block *block; struct tcf_block *block;
struct tcf_proto *tp, __rcu **chain; struct tcf_proto *tp;
struct tcf_chain *chain;
struct tcmsg *tcm = nlmsg_data(cb->nlh); struct tcmsg *tcm = nlmsg_data(cb->nlh);
unsigned long cl = 0; unsigned long cl = 0;
const struct Qdisc_class_ops *cops; const struct Qdisc_class_ops *cops;
...@@ -615,11 +681,11 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -615,11 +681,11 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
block = cops->tcf_block(q, cl); block = cops->tcf_block(q, cl);
if (!block) if (!block)
goto errout; goto errout;
chain = block->p_filter_chain; chain = block->chain;
s_t = cb->args[0]; s_t = cb->args[0];
for (tp = rtnl_dereference(*chain), t = 0; for (tp = rtnl_dereference(chain->filter_chain), t = 0;
tp; tp = rtnl_dereference(tp->next), t++) { tp; tp = rtnl_dereference(tp->next), t++) {
if (t < s_t) if (t < s_t)
continue; continue;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment