sched: act: ife: update parameters via rcu handling

This patch changes the parameter updating via RCU and not protected by a
spinlock anymore. This reduce the time that the spinlock is being held.

Signed-off-by: Alexander Aring <aring@mojatatu.com>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Alexander Aring 2017-10-11 17:16:08 -04:00 committed by David S. Miller
parent ced273eacf
commit aa9fd9a325
2 changed files with 66 additions and 29 deletions

View File

@ -6,12 +6,18 @@
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/module.h> #include <linux/module.h>
struct tcf_ife_info { struct tcf_ife_params {
struct tc_action common;
u8 eth_dst[ETH_ALEN]; u8 eth_dst[ETH_ALEN];
u8 eth_src[ETH_ALEN]; u8 eth_src[ETH_ALEN];
u16 eth_type; u16 eth_type;
u16 flags; u16 flags;
struct rcu_head rcu;
};
struct tcf_ife_info {
struct tc_action common;
struct tcf_ife_params __rcu *params;
/* list of metaids allowed */ /* list of metaids allowed */
struct list_head metalist; struct list_head metalist;
}; };

View File

@ -406,10 +406,14 @@ static void _tcf_ife_cleanup(struct tc_action *a, int bind)
static void tcf_ife_cleanup(struct tc_action *a, int bind) static void tcf_ife_cleanup(struct tc_action *a, int bind)
{ {
struct tcf_ife_info *ife = to_ife(a); struct tcf_ife_info *ife = to_ife(a);
struct tcf_ife_params *p;
spin_lock_bh(&ife->tcf_lock); spin_lock_bh(&ife->tcf_lock);
_tcf_ife_cleanup(a, bind); _tcf_ife_cleanup(a, bind);
spin_unlock_bh(&ife->tcf_lock); spin_unlock_bh(&ife->tcf_lock);
p = rcu_dereference_protected(ife->params, 1);
kfree_rcu(p, rcu);
} }
/* under ife->tcf_lock for existing action */ /* under ife->tcf_lock for existing action */
@ -446,6 +450,7 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
struct tc_action_net *tn = net_generic(net, ife_net_id); struct tc_action_net *tn = net_generic(net, ife_net_id);
struct nlattr *tb[TCA_IFE_MAX + 1]; struct nlattr *tb[TCA_IFE_MAX + 1];
struct nlattr *tb2[IFE_META_MAX + 1]; struct nlattr *tb2[IFE_META_MAX + 1];
struct tcf_ife_params *p, *p_old;
struct tcf_ife_info *ife; struct tcf_ife_info *ife;
u16 ife_type = ETH_P_IFE; u16 ife_type = ETH_P_IFE;
struct tc_ife *parm; struct tc_ife *parm;
@ -471,24 +476,34 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
if (parm->flags & ~IFE_ENCODE) if (parm->flags & ~IFE_ENCODE)
return -EINVAL; return -EINVAL;
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p)
return -ENOMEM;
exists = tcf_idr_check(tn, parm->index, a, bind); exists = tcf_idr_check(tn, parm->index, a, bind);
if (exists && bind) if (exists && bind) {
kfree(p);
return 0; return 0;
}
if (!exists) { if (!exists) {
ret = tcf_idr_create(tn, parm->index, est, a, &act_ife_ops, ret = tcf_idr_create(tn, parm->index, est, a, &act_ife_ops,
bind, true); bind, true);
if (ret) if (ret) {
kfree(p);
return ret; return ret;
}
ret = ACT_P_CREATED; ret = ACT_P_CREATED;
} else { } else {
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
if (!ovr) if (!ovr) {
kfree(p);
return -EEXIST; return -EEXIST;
}
} }
ife = to_ife(*a); ife = to_ife(*a);
ife->flags = parm->flags; p->flags = parm->flags;
if (parm->flags & IFE_ENCODE) { if (parm->flags & IFE_ENCODE) {
if (tb[TCA_IFE_TYPE]) if (tb[TCA_IFE_TYPE])
@ -499,24 +514,25 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
saddr = nla_data(tb[TCA_IFE_SMAC]); saddr = nla_data(tb[TCA_IFE_SMAC]);
} }
if (exists)
spin_lock_bh(&ife->tcf_lock);
ife->tcf_action = parm->action; ife->tcf_action = parm->action;
if (parm->flags & IFE_ENCODE) { if (parm->flags & IFE_ENCODE) {
if (daddr) if (daddr)
ether_addr_copy(ife->eth_dst, daddr); ether_addr_copy(p->eth_dst, daddr);
else else
eth_zero_addr(ife->eth_dst); eth_zero_addr(p->eth_dst);
if (saddr) if (saddr)
ether_addr_copy(ife->eth_src, saddr); ether_addr_copy(p->eth_src, saddr);
else else
eth_zero_addr(ife->eth_src); eth_zero_addr(p->eth_src);
ife->eth_type = ife_type; p->eth_type = ife_type;
} }
if (exists)
spin_lock_bh(&ife->tcf_lock);
if (ret == ACT_P_CREATED) if (ret == ACT_P_CREATED)
INIT_LIST_HEAD(&ife->metalist); INIT_LIST_HEAD(&ife->metalist);
@ -532,6 +548,7 @@ metadata_parse_err:
if (exists) if (exists)
spin_unlock_bh(&ife->tcf_lock); spin_unlock_bh(&ife->tcf_lock);
kfree(p);
return err; return err;
} }
@ -552,6 +569,7 @@ metadata_parse_err:
if (exists) if (exists)
spin_unlock_bh(&ife->tcf_lock); spin_unlock_bh(&ife->tcf_lock);
kfree(p);
return err; return err;
} }
} }
@ -559,6 +577,11 @@ metadata_parse_err:
if (exists) if (exists)
spin_unlock_bh(&ife->tcf_lock); spin_unlock_bh(&ife->tcf_lock);
p_old = rtnl_dereference(ife->params);
rcu_assign_pointer(ife->params, p);
if (p_old)
kfree_rcu(p_old, rcu);
if (ret == ACT_P_CREATED) if (ret == ACT_P_CREATED)
tcf_idr_insert(tn, *a); tcf_idr_insert(tn, *a);
@ -570,12 +593,13 @@ static int tcf_ife_dump(struct sk_buff *skb, struct tc_action *a, int bind,
{ {
unsigned char *b = skb_tail_pointer(skb); unsigned char *b = skb_tail_pointer(skb);
struct tcf_ife_info *ife = to_ife(a); struct tcf_ife_info *ife = to_ife(a);
struct tcf_ife_params *p = rtnl_dereference(ife->params);
struct tc_ife opt = { struct tc_ife opt = {
.index = ife->tcf_index, .index = ife->tcf_index,
.refcnt = ife->tcf_refcnt - ref, .refcnt = ife->tcf_refcnt - ref,
.bindcnt = ife->tcf_bindcnt - bind, .bindcnt = ife->tcf_bindcnt - bind,
.action = ife->tcf_action, .action = ife->tcf_action,
.flags = ife->flags, .flags = p->flags,
}; };
struct tcf_t t; struct tcf_t t;
@ -586,17 +610,17 @@ static int tcf_ife_dump(struct sk_buff *skb, struct tc_action *a, int bind,
if (nla_put_64bit(skb, TCA_IFE_TM, sizeof(t), &t, TCA_IFE_PAD)) if (nla_put_64bit(skb, TCA_IFE_TM, sizeof(t), &t, TCA_IFE_PAD))
goto nla_put_failure; goto nla_put_failure;
if (!is_zero_ether_addr(ife->eth_dst)) { if (!is_zero_ether_addr(p->eth_dst)) {
if (nla_put(skb, TCA_IFE_DMAC, ETH_ALEN, ife->eth_dst)) if (nla_put(skb, TCA_IFE_DMAC, ETH_ALEN, p->eth_dst))
goto nla_put_failure; goto nla_put_failure;
} }
if (!is_zero_ether_addr(ife->eth_src)) { if (!is_zero_ether_addr(p->eth_src)) {
if (nla_put(skb, TCA_IFE_SMAC, ETH_ALEN, ife->eth_src)) if (nla_put(skb, TCA_IFE_SMAC, ETH_ALEN, p->eth_src))
goto nla_put_failure; goto nla_put_failure;
} }
if (nla_put(skb, TCA_IFE_TYPE, 2, &ife->eth_type)) if (nla_put(skb, TCA_IFE_TYPE, 2, &p->eth_type))
goto nla_put_failure; goto nla_put_failure;
if (dump_metalist(skb, ife)) { if (dump_metalist(skb, ife)) {
@ -698,7 +722,7 @@ static int ife_get_sz(struct sk_buff *skb, struct tcf_ife_info *ife)
} }
static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a, static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a,
struct tcf_result *res) struct tcf_result *res, struct tcf_ife_params *p)
{ {
struct tcf_ife_info *ife = to_ife(a); struct tcf_ife_info *ife = to_ife(a);
int action = ife->tcf_action; int action = ife->tcf_action;
@ -762,19 +786,18 @@ static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a,
} }
skboff += err; skboff += err;
} }
spin_unlock(&ife->tcf_lock);
oethh = (struct ethhdr *)skb->data; oethh = (struct ethhdr *)skb->data;
if (!is_zero_ether_addr(ife->eth_src)) if (!is_zero_ether_addr(p->eth_src))
ether_addr_copy(oethh->h_source, ife->eth_src); ether_addr_copy(oethh->h_source, p->eth_src);
if (!is_zero_ether_addr(ife->eth_dst)) if (!is_zero_ether_addr(p->eth_dst))
ether_addr_copy(oethh->h_dest, ife->eth_dst); ether_addr_copy(oethh->h_dest, p->eth_dst);
oethh->h_proto = htons(ife->eth_type); oethh->h_proto = htons(p->eth_type);
if (skb_at_tc_ingress(skb)) if (skb_at_tc_ingress(skb))
skb_pull(skb, skb->dev->hard_header_len); skb_pull(skb, skb->dev->hard_header_len);
spin_unlock(&ife->tcf_lock);
return action; return action;
} }
@ -782,9 +805,17 @@ static int tcf_ife_act(struct sk_buff *skb, const struct tc_action *a,
struct tcf_result *res) struct tcf_result *res)
{ {
struct tcf_ife_info *ife = to_ife(a); struct tcf_ife_info *ife = to_ife(a);
struct tcf_ife_params *p;
int ret;
if (ife->flags & IFE_ENCODE) rcu_read_lock();
return tcf_ife_encode(skb, a, res); p = rcu_dereference(ife->params);
if (p->flags & IFE_ENCODE) {
ret = tcf_ife_encode(skb, a, res, p);
rcu_read_unlock();
return ret;
}
rcu_read_unlock();
return tcf_ife_decode(skb, a, res); return tcf_ife_decode(skb, a, res);
} }