datapth: Suppress error messages on megaflow updates
[sliver-openvswitch.git] / datapath / datapath.c
index 5c1927e..5f1b34c 100644 (file)
@@ -56,6 +56,7 @@
 
 #include "datapath.h"
 #include "flow.h"
+#include "flow_table.h"
 #include "flow_netlink.h"
 #include "vlan.h"
 #include "vport-internal_dev.h"
@@ -162,7 +163,6 @@ static void destroy_dp_rcu(struct rcu_head *rcu)
 {
        struct datapath *dp = container_of(rcu, struct datapath, rcu);
 
-       ovs_flow_tbl_destroy(&dp->table);
        free_percpu(dp->stats_percpu);
        release_net(ovs_dp_get_net(dp));
        kfree(dp->ports);
@@ -313,7 +313,7 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
        struct sk_buff *segs, *nskb;
        int err;
 
-       segs = __skb_gso_segment(skb, NETIF_F_SG | NETIF_F_HW_CSUM, false);
+       segs = __skb_gso_segment(skb, NETIF_F_SG, false);
        if (IS_ERR(segs))
                return PTR_ERR(segs);
 
@@ -373,11 +373,11 @@ static size_t key_attr_size(void)
                + nla_total_size(28); /* OVS_KEY_ATTR_ND */
 }
 
-static size_t upcall_msg_size(const struct sk_buff *skb,
-                             const struct nlattr *userdata)
+static size_t upcall_msg_size(const struct nlattr *userdata,
+                             unsigned int hdrlen)
 {
        size_t size = NLMSG_ALIGN(sizeof(struct ovs_header))
-               + nla_total_size(skb->len) /* OVS_PACKET_ATTR_PACKET */
+               + nla_total_size(hdrlen) /* OVS_PACKET_ATTR_PACKET */
                + nla_total_size(key_attr_size()); /* OVS_PACKET_ATTR_KEY */
 
        /* OVS_PACKET_ATTR_USERDATA */
@@ -400,7 +400,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
 #endif
                .snd_portid = upcall_info->portid,
        };
-       size_t len;
+       size_t len, plen;
+       unsigned int hlen;
        int err, dp_ifindex;
 
        dp_ifindex = get_dpifindex(dp);
@@ -426,7 +427,21 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
                goto out;
        }
 
-       len = upcall_msg_size(skb, upcall_info->userdata);
+       /* Complete checksum if needed */
+       if (skb->ip_summed == CHECKSUM_PARTIAL &&
+           (err = skb_checksum_help(skb)))
+               goto out;
+
+       /* Older versions of OVS user space enforce alignment of the last
+        * Netlink attribute to NLA_ALIGNTO which would require extensive
+        * padding logic. Only perform zerocopy if padding is not required.
+        */
+       if (dp->user_features & OVS_DP_F_UNALIGNED)
+               hlen = skb_zerocopy_headlen(skb);
+       else
+               hlen = skb->len;
+
+       len = upcall_msg_size(upcall_info->userdata, hlen);
        user_skb = genlmsg_new_unicast(len, &info, GFP_ATOMIC);
        if (!user_skb) {
                err = -ENOMEM;
@@ -446,13 +461,24 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
                          nla_len(upcall_info->userdata),
                          nla_data(upcall_info->userdata));
 
-       nla = __nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, skb->len);
+       /* Only reserve room for attribute header, packet data is added
+        * in skb_zerocopy() */
+       if (!(nla = nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, 0))) {
+               err = -ENOBUFS;
+               goto out;
+       }
+       nla->nla_len = nla_attr_size(skb->len);
 
-       skb_copy_and_csum_dev(skb, nla_data(nla));
+       skb_zerocopy(user_skb, skb, skb->len, hlen);
 
-       genlmsg_end(user_skb, upcall);
-       err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid);
+       /* Pad OVS_PACKET_ATTR_PACKET if linear copy was performed */
+       if (!(dp->user_features & OVS_DP_F_UNALIGNED) &&
+           (plen = (ALIGN(user_skb->len, NLA_ALIGNTO) - user_skb->len)) > 0)
+               memset(skb_put(user_skb, plen), 0, plen);
+
+       ((struct nlmsghdr *) user_skb->data)->nlmsg_len = user_skb->len;
 
+       err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid);
 out:
        kfree_skb(nskb);
        return err;
@@ -836,11 +862,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                        goto err_unlock_ovs;
 
                /* The unmasked key has to be the same for flow updates. */
-               error = -EINVAL;
-               if (!ovs_flow_cmp_unmasked_key(flow, &match)) {
-                       OVS_NLERR("Flow modification message rejected, unmasked key does not match.\n");
+               if (!ovs_flow_cmp_unmasked_key(flow, &match))
                        goto err_unlock_ovs;
-               }
 
                /* Update actions. */
                old_acts = ovsl_dereference(flow->sf_acts);
@@ -1063,6 +1086,7 @@ static size_t ovs_dp_cmd_msg_size(void)
        msgsize += nla_total_size(IFNAMSIZ);
        msgsize += nla_total_size(sizeof(struct ovs_dp_stats));
        msgsize += nla_total_size(sizeof(struct ovs_dp_megaflow_stats));
+       msgsize += nla_total_size(sizeof(u32)); /* OVS_DP_ATTR_USER_FEATURES */
 
        return msgsize;
 }
@@ -1257,7 +1281,7 @@ err_destroy_ports_array:
 err_destroy_percpu:
        free_percpu(dp->stats_percpu);
 err_destroy_table:
-       ovs_flow_tbl_destroy(&dp->table);
+       ovs_flow_tbl_destroy(&dp->table, false);
 err_free_dp:
        release_net(ovs_dp_get_net(dp));
        kfree(dp);
@@ -1284,10 +1308,13 @@ static void __dp_destroy(struct datapath *dp)
        list_del_rcu(&dp->list_node);
 
        /* OVSP_LOCAL is datapath internal port. We need to make sure that
-        * all port in datapath are destroyed first before freeing datapath.
-        */
+        * all ports in datapath are destroyed first before freeing datapath.
+        */
        ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL));
 
+       /* RCU destroy the flow table */
+       ovs_flow_tbl_destroy(&dp->table, true);
+
        call_rcu(&dp->rcu, destroy_dp_rcu);
 }