sched: act: ife: update parameters via rcu handling
authorAlexander Aring <aring@mojatatu.com>
Wed, 11 Oct 2017 21:16:08 +0000 (17:16 -0400)
committerDavid S. Miller <davem@davemloft.net>
Fri, 13 Oct 2017 05:23:03 +0000 (22:23 -0700)
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>
include/net/tc_act/tc_ife.h
net/sched/act_ife.c

index 104578f160623600aff3a34f193e04ed8f363d4e..c7fb99c3f76c0de2581a6808e8576b209e1dfa69 100644 (file)
@@ -6,12 +6,18 @@
 #include <linux/rtnetlink.h>
 #include <linux/module.h>
 
-struct tcf_ife_info {
-       struct tc_action common;
+struct tcf_ife_params {
        u8 eth_dst[ETH_ALEN];
        u8 eth_src[ETH_ALEN];
        u16 eth_type;
        u16 flags;
+
+       struct rcu_head rcu;
+};
+
+struct tcf_ife_info {
+       struct tc_action common;
+       struct tcf_ife_params __rcu *params;
        /* list of metaids allowed */
        struct list_head metalist;
 };
index f59d78918cf936c3ac3068124c356fd807034344..252ee7d8c731d5696bc328dc80afc2a64293f37e 100644 (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)
 {
        struct tcf_ife_info *ife = to_ife(a);
+       struct tcf_ife_params *p;
 
        spin_lock_bh(&ife->tcf_lock);
        _tcf_ife_cleanup(a, bind);
        spin_unlock_bh(&ife->tcf_lock);
+
+       p = rcu_dereference_protected(ife->params, 1);
+       kfree_rcu(p, rcu);
 }
 
 /* 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 nlattr *tb[TCA_IFE_MAX + 1];
        struct nlattr *tb2[IFE_META_MAX + 1];
+       struct tcf_ife_params *p, *p_old;
        struct tcf_ife_info *ife;
        u16 ife_type = ETH_P_IFE;
        struct tc_ife *parm;
@@ -471,24 +476,34 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
        if (parm->flags & ~IFE_ENCODE)
                return -EINVAL;
 
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+
        exists = tcf_idr_check(tn, parm->index, a, bind);
-       if (exists && bind)
+       if (exists && bind) {
+               kfree(p);
                return 0;
+       }
 
        if (!exists) {
                ret = tcf_idr_create(tn, parm->index, est, a, &act_ife_ops,
                                     bind, true);
-               if (ret)
+               if (ret) {
+                       kfree(p);
                        return ret;
+               }
                ret = ACT_P_CREATED;
        } else {
                tcf_idr_release(*a, bind);
-               if (!ovr)
+               if (!ovr) {
+                       kfree(p);
                        return -EEXIST;
+               }
        }
 
        ife = to_ife(*a);
-       ife->flags = parm->flags;
+       p->flags = parm->flags;
 
        if (parm->flags & IFE_ENCODE) {
                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]);
        }
 
-       if (exists)
-               spin_lock_bh(&ife->tcf_lock);
        ife->tcf_action = parm->action;
 
        if (parm->flags & IFE_ENCODE) {
                if (daddr)
-                       ether_addr_copy(ife->eth_dst, daddr);
+                       ether_addr_copy(p->eth_dst, daddr);
                else
-                       eth_zero_addr(ife->eth_dst);
+                       eth_zero_addr(p->eth_dst);
 
                if (saddr)
-                       ether_addr_copy(ife->eth_src, saddr);
+                       ether_addr_copy(p->eth_src, saddr);
                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)
                INIT_LIST_HEAD(&ife->metalist);
 
@@ -532,6 +548,7 @@ metadata_parse_err:
 
                        if (exists)
                                spin_unlock_bh(&ife->tcf_lock);
+                       kfree(p);
                        return err;
                }
 
@@ -552,6 +569,7 @@ metadata_parse_err:
 
                        if (exists)
                                spin_unlock_bh(&ife->tcf_lock);
+                       kfree(p);
                        return err;
                }
        }
@@ -559,6 +577,11 @@ metadata_parse_err:
        if (exists)
                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)
                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);
        struct tcf_ife_info *ife = to_ife(a);
+       struct tcf_ife_params *p = rtnl_dereference(ife->params);
        struct tc_ife opt = {
                .index = ife->tcf_index,
                .refcnt = ife->tcf_refcnt - ref,
                .bindcnt = ife->tcf_bindcnt - bind,
                .action = ife->tcf_action,
-               .flags = ife->flags,
+               .flags = p->flags,
        };
        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))
                goto nla_put_failure;
 
-       if (!is_zero_ether_addr(ife->eth_dst)) {
-               if (nla_put(skb, TCA_IFE_DMAC, ETH_ALEN, ife->eth_dst))
+       if (!is_zero_ether_addr(p->eth_dst)) {
+               if (nla_put(skb, TCA_IFE_DMAC, ETH_ALEN, p->eth_dst))
                        goto nla_put_failure;
        }
 
-       if (!is_zero_ether_addr(ife->eth_src)) {
-               if (nla_put(skb, TCA_IFE_SMAC, ETH_ALEN, ife->eth_src))
+       if (!is_zero_ether_addr(p->eth_src)) {
+               if (nla_put(skb, TCA_IFE_SMAC, ETH_ALEN, p->eth_src))
                        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;
 
        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,
-                         struct tcf_result *res)
+                         struct tcf_result *res, struct tcf_ife_params *p)
 {
        struct tcf_ife_info *ife = to_ife(a);
        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;
        }
+       spin_unlock(&ife->tcf_lock);
        oethh = (struct ethhdr *)skb->data;
 
-       if (!is_zero_ether_addr(ife->eth_src))
-               ether_addr_copy(oethh->h_source, ife->eth_src);
-       if (!is_zero_ether_addr(ife->eth_dst))
-               ether_addr_copy(oethh->h_dest, ife->eth_dst);
-       oethh->h_proto = htons(ife->eth_type);
+       if (!is_zero_ether_addr(p->eth_src))
+               ether_addr_copy(oethh->h_source, p->eth_src);
+       if (!is_zero_ether_addr(p->eth_dst))
+               ether_addr_copy(oethh->h_dest, p->eth_dst);
+       oethh->h_proto = htons(p->eth_type);
 
        if (skb_at_tc_ingress(skb))
                skb_pull(skb, skb->dev->hard_header_len);
 
-       spin_unlock(&ife->tcf_lock);
-
        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_ife_info *ife = to_ife(a);
-
-       if (ife->flags & IFE_ENCODE)
-               return tcf_ife_encode(skb, a, res);
+       struct tcf_ife_params *p;
+       int ret;
+
+       rcu_read_lock();
+       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);
 }