]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/core/fib_rules.c
Merge branch 'master' into tk71
[mv-sheeva.git] / net / core / fib_rules.c
index 42e84e08a1becd4b64a65a74758bf4e13783a8ae..a20e5d3bbfa017db76959cfd448962c134d5d309 100644 (file)
@@ -144,7 +144,7 @@ fib_rules_register(const struct fib_rules_ops *tmpl, struct net *net)
 }
 EXPORT_SYMBOL_GPL(fib_rules_register);
 
-void fib_rules_cleanup_ops(struct fib_rules_ops *ops)
+static void fib_rules_cleanup_ops(struct fib_rules_ops *ops)
 {
        struct fib_rule *rule, *tmp;
 
@@ -153,7 +153,6 @@ void fib_rules_cleanup_ops(struct fib_rules_ops *ops)
                fib_rule_put(rule);
        }
 }
-EXPORT_SYMBOL_GPL(fib_rules_cleanup_ops);
 
 static void fib_rules_put_rcu(struct rcu_head *head)
 {
@@ -225,9 +224,12 @@ jumped:
                        err = ops->action(rule, fl, flags, arg);
 
                if (err != -EAGAIN) {
-                       fib_rule_get(rule);
-                       arg->rule = rule;
-                       goto out;
+                       if ((arg->flags & FIB_LOOKUP_NOREF) ||
+                           likely(atomic_inc_not_zero(&rule->refcnt))) {
+                               arg->rule = rule;
+                               goto out;
+                       }
+                       break;
                }
        }
 
@@ -348,12 +350,12 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 
                list_for_each_entry(r, &ops->rules_list, list) {
                        if (r->pref == rule->target) {
-                               rule->ctarget = r;
+                               RCU_INIT_POINTER(rule->ctarget, r);
                                break;
                        }
                }
 
-               if (rule->ctarget == NULL)
+               if (rcu_dereference_protected(rule->ctarget, 1) == NULL)
                        unresolved = 1;
        } else if (rule->action == FR_ACT_GOTO)
                goto errout_free;
@@ -370,6 +372,11 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 
        fib_rule_get(rule);
 
+       if (last)
+               list_add_rcu(&rule->list, &last->list);
+       else
+               list_add_rcu(&rule->list, &ops->rules_list);
+
        if (ops->unresolved_rules) {
                /*
                 * There are unresolved goto rules in the list, check if
@@ -378,7 +385,7 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
                list_for_each_entry(r, &ops->rules_list, list) {
                        if (r->action == FR_ACT_GOTO &&
                            r->target == rule->pref) {
-                               BUG_ON(r->ctarget != NULL);
+                               BUG_ON(rtnl_dereference(r->ctarget) != NULL);
                                rcu_assign_pointer(r->ctarget, rule);
                                if (--ops->unresolved_rules == 0)
                                        break;
@@ -392,11 +399,6 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        if (unresolved)
                ops->unresolved_rules++;
 
-       if (last)
-               list_add_rcu(&rule->list, &last->list);
-       else
-               list_add_rcu(&rule->list, &ops->rules_list);
-
        notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).pid);
        flush_route_cache(ops);
        rules_ops_put(ops);
@@ -484,14 +486,13 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
                 */
                if (ops->nr_goto_rules > 0) {
                        list_for_each_entry(tmp, &ops->rules_list, list) {
-                               if (tmp->ctarget == rule) {
+                               if (rtnl_dereference(tmp->ctarget) == rule) {
                                        rcu_assign_pointer(tmp->ctarget, NULL);
                                        ops->unresolved_rules++;
                                }
                        }
                }
 
-               synchronize_rcu();
                notify_rule_change(RTM_DELRULE, rule, ops, nlh,
                                   NETLINK_CB(skb).pid);
                fib_rule_put(rule);
@@ -543,7 +544,8 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
        frh->action = rule->action;
        frh->flags = rule->flags;
 
-       if (rule->action == FR_ACT_GOTO && rule->ctarget == NULL)
+       if (rule->action == FR_ACT_GOTO &&
+           rcu_dereference_raw(rule->ctarget) == NULL)
                frh->flags |= FIB_RULE_UNRESOLVED;
 
        if (rule->iifname[0]) {