openvswitch: Make tunnel set action attach a metadata dst
authorThomas Graf <tgraf@suug.ch>
Tue, 21 Jul 2015 08:44:03 +0000 (10:44 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 21 Jul 2015 17:39:06 +0000 (10:39 -0700)
Utilize the new metadata dst to attach encapsulation instructions to
the skb. The existing egress_tun_info via the OVS_CB() is left in
place until all tunnel vports have been converted to the new method.

Signed-off-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/openvswitch/actions.c
net/openvswitch/datapath.c
net/openvswitch/flow.h
net/openvswitch/flow_netlink.c
net/openvswitch/flow_netlink.h
net/openvswitch/flow_table.c

index 27c1687cfd927252907dfe7da8f5886771856b49..cf04c2f8b32a57bfec8e024d2db95dd8c0468b3f 100644 (file)
@@ -733,7 +733,15 @@ static int execute_set_action(struct sk_buff *skb,
 {
        /* Only tunnel set execution is supported without a mask. */
        if (nla_type(a) == OVS_KEY_ATTR_TUNNEL_INFO) {
-               OVS_CB(skb)->egress_tun_info = nla_data(a);
+               struct ovs_tunnel_info *tun = nla_data(a);
+
+               skb_dst_drop(skb);
+               dst_hold((struct dst_entry *)tun->tun_dst);
+               skb_dst_set(skb, (struct dst_entry *)tun->tun_dst);
+
+               /* FIXME: Remove when all vports have been converted */
+               OVS_CB(skb)->egress_tun_info = &tun->tun_dst->u.tun_info;
+
                return 0;
        }
 
index ff8c4a4c160986bf206f4751860f9767e71246bf..02082107c74cea418d85c35012237eb01763fcfc 100644 (file)
@@ -1018,7 +1018,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
                }
                ovs_unlock();
 
-               ovs_nla_free_flow_actions(old_acts);
+               ovs_nla_free_flow_actions_rcu(old_acts);
                ovs_flow_free(new_flow, false);
        }
 
@@ -1030,7 +1030,7 @@ err_unlock_ovs:
        ovs_unlock();
        kfree_skb(reply);
 err_kfree_acts:
-       kfree(acts);
+       ovs_nla_free_flow_actions(acts);
 err_kfree_flow:
        ovs_flow_free(new_flow, false);
 error:
@@ -1157,7 +1157,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
        if (reply)
                ovs_notify(&dp_flow_genl_family, reply, info);
        if (old_acts)
-               ovs_nla_free_flow_actions(old_acts);
+               ovs_nla_free_flow_actions_rcu(old_acts);
 
        return 0;
 
@@ -1165,7 +1165,7 @@ err_unlock_ovs:
        ovs_unlock();
        kfree_skb(reply);
 err_kfree_acts:
-       kfree(acts);
+       ovs_nla_free_flow_actions(acts);
 error:
        return error;
 }
index cadc6c5c35456c9c174af9446b58e98505926e79..b62cdb3e35892aa56468fbc17b1cd0ef3450af6a 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/flex_array.h>
 #include <net/inet_ecn.h>
 #include <net/ip_tunnels.h>
+#include <net/dst_metadata.h>
 
 struct sk_buff;
 
@@ -45,6 +46,10 @@ struct sk_buff;
 #define TUN_METADATA_OPTS(flow_key, opt_len) \
        ((void *)((flow_key)->tun_opts + TUN_METADATA_OFFSET(opt_len)))
 
+struct ovs_tunnel_info {
+       struct metadata_dst     *tun_dst;
+};
+
 #define OVS_SW_FLOW_KEY_METADATA_SIZE                  \
        (offsetof(struct sw_flow_key, recirc_id) +      \
        FIELD_SIZEOF(struct sw_flow_key, recirc_id))
index ecfa530d3461f7d7ab838e58c4472259e36c0352..e7906dfb8814f73cd75a1d2fa5ccc2f4426c1201 100644 (file)
@@ -1548,11 +1548,48 @@ static struct sw_flow_actions *nla_alloc_flow_actions(int size, bool log)
        return sfa;
 }
 
+static void ovs_nla_free_set_action(const struct nlattr *a)
+{
+       const struct nlattr *ovs_key = nla_data(a);
+       struct ovs_tunnel_info *ovs_tun;
+
+       switch (nla_type(ovs_key)) {
+       case OVS_KEY_ATTR_TUNNEL_INFO:
+               ovs_tun = nla_data(ovs_key);
+               dst_release((struct dst_entry *)ovs_tun->tun_dst);
+               break;
+       }
+}
+
+void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts)
+{
+       const struct nlattr *a;
+       int rem;
+
+       if (!sf_acts)
+               return;
+
+       nla_for_each_attr(a, sf_acts->actions, sf_acts->actions_len, rem) {
+               switch (nla_type(a)) {
+               case OVS_ACTION_ATTR_SET:
+                       ovs_nla_free_set_action(a);
+                       break;
+               }
+       }
+
+       kfree(sf_acts);
+}
+
+static void __ovs_nla_free_flow_actions(struct rcu_head *head)
+{
+       ovs_nla_free_flow_actions(container_of(head, struct sw_flow_actions, rcu));
+}
+
 /* Schedules 'sf_acts' to be freed after the next RCU grace period.
  * The caller must hold rcu_read_lock for this to be sensible. */
-void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts)
+void ovs_nla_free_flow_actions_rcu(struct sw_flow_actions *sf_acts)
 {
-       kfree_rcu(sf_acts, rcu);
+       call_rcu(&sf_acts->rcu, __ovs_nla_free_flow_actions);
 }
 
 static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa,
@@ -1746,7 +1783,9 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
 {
        struct sw_flow_match match;
        struct sw_flow_key key;
+       struct metadata_dst *tun_dst;
        struct ip_tunnel_info *tun_info;
+       struct ovs_tunnel_info *ovs_tun;
        struct nlattr *a;
        int err = 0, start, opts_type;
 
@@ -1771,12 +1810,22 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
        if (start < 0)
                return start;
 
+       tun_dst = metadata_dst_alloc(key.tun_opts_len, GFP_KERNEL);
+       if (!tun_dst)
+               return -ENOMEM;
+
        a = __add_action(sfa, OVS_KEY_ATTR_TUNNEL_INFO, NULL,
-                        sizeof(*tun_info) + key.tun_opts_len, log);
-       if (IS_ERR(a))
+                        sizeof(*ovs_tun), log);
+       if (IS_ERR(a)) {
+               dst_release((struct dst_entry *)tun_dst);
                return PTR_ERR(a);
+       }
+
+       ovs_tun = nla_data(a);
+       ovs_tun->tun_dst = tun_dst;
 
-       tun_info = nla_data(a);
+       tun_info = &tun_dst->u.tun_info;
+       tun_info->mode = IP_TUNNEL_INFO_TX;
        tun_info->key = key.tun_key;
        tun_info->options_len = key.tun_opts_len;
 
@@ -2177,7 +2226,7 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
        err = __ovs_nla_copy_actions(attr, key, 0, sfa, key->eth.type,
                                     key->eth.tci, log);
        if (err)
-               kfree(*sfa);
+               ovs_nla_free_flow_actions(*sfa);
 
        return err;
 }
@@ -2227,7 +2276,8 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
 
        switch (key_type) {
        case OVS_KEY_ATTR_TUNNEL_INFO: {
-               struct ip_tunnel_info *tun_info = nla_data(ovs_key);
+               struct ovs_tunnel_info *ovs_tun = nla_data(ovs_key);
+               struct ip_tunnel_info *tun_info = &ovs_tun->tun_dst->u.tun_info;
 
                start = nla_nest_start(skb, OVS_ACTION_ATTR_SET);
                if (!start)
index ec53eb6e632b9d3aa378599dbbb5508b428d97e9..acd074408f0aa2167410e3dbf6a05052ff3b4a4b 100644 (file)
@@ -69,5 +69,6 @@ int ovs_nla_put_actions(const struct nlattr *attr,
                        int len, struct sk_buff *skb);
 
 void ovs_nla_free_flow_actions(struct sw_flow_actions *);
+void ovs_nla_free_flow_actions_rcu(struct sw_flow_actions *);
 
 #endif /* flow_netlink.h */
index 4613df8c82900e32a4e0188688c9e0354022484d..b70d845e7efb8300a0d26fe7ba16720f62481337 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "flow.h"
 #include "datapath.h"
+#include "flow_netlink.h"
 #include <linux/uaccess.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -143,7 +144,8 @@ static void flow_free(struct sw_flow *flow)
 
        if (ovs_identifier_is_key(&flow->id))
                kfree(flow->id.unmasked_key);
-       kfree((struct sw_flow_actions __force *)flow->sf_acts);
+       if (flow->sf_acts)
+               ovs_nla_free_flow_actions((struct sw_flow_actions __force *)flow->sf_acts);
        for_each_node(node)
                if (flow->stats[node])
                        kmem_cache_free(flow_stats_cache,