net/sched: act_ct: Software offload of established flows
[linux-2.6-microblaze.git] / net / sched / cls_api.c
index 76e0d12..4e766c5 100644 (file)
@@ -2055,9 +2055,8 @@ replay:
                                                               &chain_info));
 
                mutex_unlock(&chain->filter_chain_lock);
-               tp_new = tcf_proto_create(nla_data(tca[TCA_KIND]),
-                                         protocol, prio, chain, rtnl_held,
-                                         extack);
+               tp_new = tcf_proto_create(name, protocol, prio, chain,
+                                         rtnl_held, extack);
                if (IS_ERR(tp_new)) {
                        err = PTR_ERR(tp_new);
                        goto errout_tp;
@@ -3383,14 +3382,40 @@ int tc_setup_cb_reoffload(struct tcf_block *block, struct tcf_proto *tp,
 }
 EXPORT_SYMBOL(tc_setup_cb_reoffload);
 
+static int tcf_act_get_cookie(struct flow_action_entry *entry,
+                             const struct tc_action *act)
+{
+       struct tc_cookie *cookie;
+       int err = 0;
+
+       rcu_read_lock();
+       cookie = rcu_dereference(act->act_cookie);
+       if (cookie) {
+               entry->cookie = flow_action_cookie_create(cookie->data,
+                                                         cookie->len,
+                                                         GFP_ATOMIC);
+               if (!entry->cookie)
+                       err = -ENOMEM;
+       }
+       rcu_read_unlock();
+       return err;
+}
+
+static void tcf_act_put_cookie(struct flow_action_entry *entry)
+{
+       flow_action_cookie_destroy(entry->cookie);
+}
+
 void tc_cleanup_flow_action(struct flow_action *flow_action)
 {
        struct flow_action_entry *entry;
        int i;
 
-       flow_action_for_each(i, entry, flow_action)
+       flow_action_for_each(i, entry, flow_action) {
+               tcf_act_put_cookie(entry);
                if (entry->destructor)
                        entry->destructor(entry->destructor_priv);
+       }
 }
 EXPORT_SYMBOL(tc_cleanup_flow_action);
 
@@ -3434,22 +3459,23 @@ static void tcf_sample_get_group(struct flow_action_entry *entry,
 }
 
 int tc_setup_flow_action(struct flow_action *flow_action,
-                        const struct tcf_exts *exts, bool rtnl_held)
+                        const struct tcf_exts *exts)
 {
-       const struct tc_action *act;
+       struct tc_action *act;
        int i, j, k, err = 0;
 
        if (!exts)
                return 0;
 
-       if (!rtnl_held)
-               rtnl_lock();
-
        j = 0;
        tcf_exts_for_each_action(i, act, exts) {
                struct flow_action_entry *entry;
 
                entry = &flow_action->entries[j];
+               spin_lock_bh(&act->tcfa_lock);
+               err = tcf_act_get_cookie(entry, act);
+               if (err)
+                       goto err_out_locked;
                if (is_tcf_gact_ok(act)) {
                        entry->id = FLOW_ACTION_ACCEPT;
                } else if (is_tcf_gact_shot(act)) {
@@ -3490,13 +3516,13 @@ int tc_setup_flow_action(struct flow_action *flow_action,
                                break;
                        default:
                                err = -EOPNOTSUPP;
-                               goto err_out;
+                               goto err_out_locked;
                        }
                } else if (is_tcf_tunnel_set(act)) {
                        entry->id = FLOW_ACTION_TUNNEL_ENCAP;
                        err = tcf_tunnel_encap_get_tunnel(entry, act);
                        if (err)
-                               goto err_out;
+                               goto err_out_locked;
                } else if (is_tcf_tunnel_release(act)) {
                        entry->id = FLOW_ACTION_TUNNEL_DECAP;
                } else if (is_tcf_pedit(act)) {
@@ -3510,7 +3536,7 @@ int tc_setup_flow_action(struct flow_action *flow_action,
                                        break;
                                default:
                                        err = -EOPNOTSUPP;
-                                       goto err_out;
+                                       goto err_out_locked;
                                }
                                entry->mangle.htype = tcf_pedit_htype(act, k);
                                entry->mangle.mask = tcf_pedit_mask(act, k);
@@ -3561,28 +3587,29 @@ int tc_setup_flow_action(struct flow_action *flow_action,
                                entry->mpls_mangle.ttl = tcf_mpls_ttl(act);
                                break;
                        default:
-                               goto err_out;
+                               goto err_out_locked;
                        }
                } else if (is_tcf_skbedit_ptype(act)) {
                        entry->id = FLOW_ACTION_PTYPE;
                        entry->ptype = tcf_skbedit_ptype(act);
                } else {
                        err = -EOPNOTSUPP;
-                       goto err_out;
+                       goto err_out_locked;
                }
+               spin_unlock_bh(&act->tcfa_lock);
 
                if (!is_tcf_pedit(act))
                        j++;
        }
 
 err_out:
-       if (!rtnl_held)
-               rtnl_unlock();
-
        if (err)
                tc_cleanup_flow_action(flow_action);
 
        return err;
+err_out_locked:
+       spin_unlock_bh(&act->tcfa_lock);
+       goto err_out;
 }
 EXPORT_SYMBOL(tc_setup_flow_action);