Following patch adds sampling action which takes probability and set
of actions as arguments. When probability is hit, actions are executed
for given packet.
USERSPACE action's userdata (u64) is used to store struct user_data as
cookie. CONTROLLER action is fixed accordingly.

Now we can remove sFlow code from kernel and implement sFlow generically
as SAMPLE action. sFlow is defined as SAMPLE Action with probability (sFlow
sampling rate) and USERSPACE action as argument. USERSPACE action's data
is used as cookie. sFlow uses this cookie to store output-port, number of
output ports and vlan-id. sample-pool is calculated by using vport
stats.

Signed-off-by: Pravin Shelar <pshe...@nicira.com>
---
 datapath/actions.c                      |   83 +++++++------
 datapath/datapath.c                     |   80 ++++++++-----
 datapath/datapath.h                     |   18 +---
 datapath/vport.c                        |    1 -
 datapath/vport.h                        |    3 -
 include/openvswitch/datapath-protocol.h |   31 +++--
 lib/dpif-linux.c                        |   59 +---------
 lib/dpif-netdev.c                       |    2 -
 lib/dpif-provider.h                     |   19 ---
 lib/dpif.c                              |   39 +------
 lib/dpif.h                              |    8 --
 lib/odp-util.c                          |   56 +++++++++-
 ofproto/ofproto-dpif-sflow.c            |   84 ++++++-------
 ofproto/ofproto-dpif-sflow.h            |   12 ++-
 ofproto/ofproto-dpif.c                  |  195 ++++++++++++++++++++++++++++---
 ofproto/ofproto.h                       |   20 +++
 16 files changed, 420 insertions(+), 290 deletions(-)

diff --git a/datapath/actions.c b/datapath/actions.c
index 83b531a..49a2fb4 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -28,8 +28,8 @@
 #include "vlan.h"
 #include "vport.h"
 
-static int do_execute_actions(struct datapath *, struct sk_buff *,
-                             struct sw_flow_actions *acts);
+static int __do_execute_actions(struct datapath *dp, struct sk_buff *skb,
+                       const struct nlattr *attr, int len, bool keep_skb);
 
 static int make_writable(struct sk_buff *skb, int write_len)
 {
@@ -256,15 +256,43 @@ static int output_userspace(struct datapath *dp, struct 
sk_buff *skb, u64 arg)
        upcall.cmd = OVS_PACKET_CMD_ACTION;
        upcall.key = &OVS_CB(skb)->flow->key;
        upcall.userdata = arg;
-       upcall.sample_pool = 0;
-       upcall.actions = NULL;
-       upcall.actions_len = 0;
        return dp_upcall(dp, skb, &upcall);
 }
 
+static int sample(struct datapath *dp, struct sk_buff *skb,
+                 const struct nlattr *attr)
+{
+       const struct nlattr *acts_list = NULL;
+       const struct nlattr *a;
+       int rem;
+
+       nla_for_each_nested(a, attr, rem) {
+
+               switch (nla_type(a)) {
+                       case OVS_SAMPLE_ATTR_PROBABILITY:
+                               if (net_random() >= nla_get_u32(a))
+                                       return 0;
+                               break;
+
+                       case OVS_SAMPLE_ATTR_ACTIONS:
+                               acts_list = nla_data(a);
+                               break;
+               }
+       }
+
+       if (acts_list) {
+               int error;
+               error = __do_execute_actions(dp, skb, acts_list,
+                               nla_len(acts_list), 1);
+               return error;
+       }
+
+       return 0;
+}
+
 /* Execute a list of actions against 'skb'. */
-static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
-                             struct sw_flow_actions *acts)
+static int __do_execute_actions(struct datapath *dp, struct sk_buff *skb,
+                       const struct nlattr *attr, int len, bool keep_skb)
 {
        /* Every output action needs a separate clone of 'skb', but the common
         * case is just a single output action, so that doing a clone and
@@ -275,7 +303,7 @@ static int do_execute_actions(struct datapath *dp, struct 
sk_buff *skb,
        const struct nlattr *a;
        int rem;
 
-       for (a = acts->actions, rem = acts->actions_len; rem > 0;
+       for (a = attr, rem = len; rem > 0;
             a = nla_next(a, &rem)) {
                int err = 0;
 
@@ -340,8 +368,12 @@ static int do_execute_actions(struct datapath *dp, struct 
sk_buff *skb,
                case OVS_ACTION_ATTR_POP_PRIORITY:
                        skb->priority = priority;
                        break;
-               }
 
+               case ODP_ACTION_ATTR_SAMPLE:
+                       err = sample(dp, skb, a);
+                       break;
+
+               }
                if (unlikely(err)) {
                        kfree_skb(skb);
                        return err;
@@ -350,37 +382,17 @@ static int do_execute_actions(struct datapath *dp, struct 
sk_buff *skb,
 
        if (prev_port != -1)
                do_output(dp, skb, prev_port);
-       else
+       else if (!keep_skb)
                consume_skb(skb);
 
        return 0;
 }
 
-static void sflow_sample(struct datapath *dp, struct sk_buff *skb,
-                        struct sw_flow_actions *acts)
-{
-       struct sk_buff *nskb;
-       struct vport *p = OVS_CB(skb)->vport;
-       struct dp_upcall_info upcall;
-
-       if (unlikely(!p))
-               return;
-
-       atomic_inc(&p->sflow_pool);
-       if (net_random() >= dp->sflow_probability)
-               return;
 
-       nskb = skb_clone(skb, GFP_ATOMIC);
-       if (unlikely(!nskb))
-               return;
-
-       upcall.cmd = OVS_PACKET_CMD_SAMPLE;
-       upcall.key = &OVS_CB(skb)->flow->key;
-       upcall.userdata = 0;
-       upcall.sample_pool = atomic_read(&p->sflow_pool);
-       upcall.actions = acts->actions;
-       upcall.actions_len = acts->actions_len;
-       dp_upcall(dp, nskb, &upcall);
+static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
+                             struct sw_flow_actions *acts)
+{
+       return __do_execute_actions(dp, skb, acts->actions, acts->actions_len, 
0);
 }
 
 /* Execute a list of actions against 'skb'. */
@@ -400,9 +412,6 @@ int execute_actions(struct datapath *dp, struct sk_buff 
*skb)
                goto out_loop;
        }
 
-       /* Really execute actions. */
-       if (dp->sflow_probability)
-               sflow_sample(dp, skb, acts);
        OVS_CB(skb)->tun_id = 0;
        error = do_execute_actions(dp, skb, acts);
 
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 1fba23b..8999a1a 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -293,10 +293,6 @@ void dp_process_received_packet(struct vport *p, struct 
sk_buff *skb)
 
                        upcall.cmd = OVS_PACKET_CMD_MISS;
                        upcall.key = &key;
-                       upcall.userdata = 0;
-                       upcall.sample_pool = 0;
-                       upcall.actions = NULL;
-                       upcall.actions_len = 0;
                        dp_upcall(dp, skb, &upcall);
                        stats_counter_off = offsetof(struct dp_stats_percpu, 
n_missed);
                        goto out;
@@ -457,12 +453,8 @@ static int queue_userspace_packets(struct datapath *dp, 
struct sk_buff *skb,
                len = sizeof(struct ovs_header);
                len += nla_total_size(skb->len);
                len += nla_total_size(FLOW_BUFSIZE);
-               if (upcall_info->userdata)
+               if (upcall_info->cmd == OVS_PACKET_CMD_ACTION)
                        len += nla_total_size(8);
-               if (upcall_info->sample_pool)
-                       len += nla_total_size(4);
-               if (upcall_info->actions_len)
-                       len += nla_total_size(upcall_info->actions_len);
 
                user_skb = genlmsg_new(len, GFP_ATOMIC);
                if (!user_skb) {
@@ -478,18 +470,8 @@ static int queue_userspace_packets(struct datapath *dp, 
struct sk_buff *skb,
                flow_to_nlattrs(upcall_info->key, user_skb);
                nla_nest_end(user_skb, nla);
 
-               if (upcall_info->userdata)
+               if (upcall_info->cmd == OVS_PACKET_CMD_ACTION)
                        nla_put_u64(user_skb, OVS_PACKET_ATTR_USERDATA, 
upcall_info->userdata);
-               if (upcall_info->sample_pool)
-                       nla_put_u32(user_skb, OVS_PACKET_ATTR_SAMPLE_POOL, 
upcall_info->sample_pool);
-               if (upcall_info->actions_len) {
-                       const struct nlattr *actions = upcall_info->actions;
-                       u32 actions_len = upcall_info->actions_len;
-
-                       nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_ACTIONS);
-                       memcpy(__skb_put(user_skb, actions_len), actions, 
actions_len);
-                       nla_nest_end(user_skb, nla);
-               }
 
                nla = __nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, skb->len);
                if (skb->ip_summed == CHECKSUM_PARTIAL)
@@ -537,12 +519,50 @@ static int flush_flows(int dp_ifindex)
        return 0;
 }
 
-static int validate_actions(const struct nlattr *attr)
+static int validate_actions(const struct nlattr *attr, int depth);
+
+static int validate_sample(const struct nlattr *attr, int depth)
 {
+       bool valid_probability = false;
        const struct nlattr *a;
        int rem;
 
        nla_for_each_nested(a, attr, rem) {
+               int error;
+               int type = nla_type(a);
+
+               switch (type) {
+                       case OVS_SAMPLE_ATTR_PROBABILITY:
+                               if (nla_len(a) != sizeof(u32))
+                                       return -EINVAL;
+                               valid_probability = true;
+                               break;
+
+                       case OVS_SAMPLE_ATTR_ACTIONS:
+                               error = validate_actions(a, (depth + 1));
+                               if (error)
+                                       return error;
+                               break;
+                       default:
+                               return -EINVAL;
+               }
+       }
+
+       if (valid_probability)
+               return 0;
+       else
+               return -EINVAL;
+}
+
+static int validate_actions(const struct nlattr *attr, int depth)
+{
+       const struct nlattr *a;
+       int rem, err;
+
+       if (depth >= MAX_ACTIONS_DEPTH)
+               return -EOVERFLOW;
+
+       nla_for_each_nested(a, attr, rem) {
                static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = {
                        [OVS_ACTION_ATTR_OUTPUT] = 4,
                        [OVS_ACTION_ATTR_USERSPACE] = 8,
@@ -558,6 +578,7 @@ static int validate_actions(const struct nlattr *attr)
                        [OVS_ACTION_ATTR_SET_TUNNEL] = 8,
                        [OVS_ACTION_ATTR_SET_PRIORITY] = 4,
                        [OVS_ACTION_ATTR_POP_PRIORITY] = 0,
+                       [ODP_ACTION_ATTR_SAMPLE] = 24,
                };
                int type = nla_type(a);
 
@@ -597,6 +618,12 @@ static int validate_actions(const struct nlattr *attr)
                                return -EINVAL;
                        break;
 
+               case ODP_ACTION_ATTR_SAMPLE:
+                       err = validate_sample(a, depth);
+                       if (err)
+                               return err;
+                       break;
+
                default:
                        return -EOPNOTSUPP;
                }
@@ -635,7 +662,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, 
struct genl_info *info)
            nla_len(a[OVS_PACKET_ATTR_PACKET]) < ETH_HLEN)
                goto err;
 
-       err = validate_actions(a[OVS_PACKET_ATTR_ACTIONS]);
+       err = validate_actions(a[OVS_PACKET_ATTR_ACTIONS], 0);
        if (err)
                goto err;
 
@@ -893,7 +920,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, 
struct genl_info *info)
 
        /* Validate actions. */
        if (a[OVS_FLOW_ATTR_ACTIONS]) {
-               error = validate_actions(a[OVS_FLOW_ATTR_ACTIONS]);
+               error = validate_actions(a[OVS_FLOW_ATTR_ACTIONS], 0);
                if (error)
                        goto error;
        } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) {
@@ -1145,7 +1172,6 @@ static const struct nla_policy 
datapath_policy[OVS_DP_ATTR_MAX + 1] = {
        [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
 #endif
        [OVS_DP_ATTR_IPV4_FRAGS] = { .type = NLA_U32 },
-       [OVS_DP_ATTR_SAMPLING] = { .type = NLA_U32 },
 };
 
 static struct genl_family dp_datapath_genl_family = {
@@ -1188,15 +1214,11 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, 
struct sk_buff *skb,
        NLA_PUT_U32(skb, OVS_DP_ATTR_IPV4_FRAGS,
                    dp->drop_frags ? OVS_DP_FRAG_DROP : OVS_DP_FRAG_ZERO);
 
-       if (dp->sflow_probability)
-               NLA_PUT_U32(skb, OVS_DP_ATTR_SAMPLING, dp->sflow_probability);
-
        nla = nla_nest_start(skb, OVS_DP_ATTR_MCGROUPS);
        if (!nla)
                goto nla_put_failure;
        NLA_PUT_U32(skb, OVS_PACKET_CMD_MISS, packet_mc_group(dp, 
OVS_PACKET_CMD_MISS));
        NLA_PUT_U32(skb, OVS_PACKET_CMD_ACTION, packet_mc_group(dp, 
OVS_PACKET_CMD_ACTION));
-       NLA_PUT_U32(skb, OVS_PACKET_CMD_SAMPLE, packet_mc_group(dp, 
OVS_PACKET_CMD_SAMPLE));
        nla_nest_end(skb, nla);
 
        return genlmsg_end(skb, ovs_header);
@@ -1260,8 +1282,6 @@ static void change_datapath(struct datapath *dp, struct 
nlattr *a[OVS_DP_ATTR_MA
 {
        if (a[OVS_DP_ATTR_IPV4_FRAGS])
                dp->drop_frags = nla_get_u32(a[OVS_DP_ATTR_IPV4_FRAGS]) == 
OVS_DP_FRAG_DROP;
-       if (a[OVS_DP_ATTR_SAMPLING])
-               dp->sflow_probability = nla_get_u32(a[OVS_DP_ATTR_SAMPLING]);
 }
 
 static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
diff --git a/datapath/datapath.h b/datapath/datapath.h
index ff8ac10..2aeb1a7 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -34,6 +34,8 @@ struct vport;
 
 #define DP_MAX_PORTS 1024
 
+#define MAX_ACTIONS_DEPTH 3
+
 /**
  * struct dp_stats_percpu - per-cpu packet processing statistics for a given
  * datapath.
@@ -69,9 +71,6 @@ struct dp_stats_percpu {
  * @port_list: List of all ports in @ports in arbitrary order.  RTNL required
  * to iterate or modify.
  * @stats_percpu: Per-CPU datapath statistics.
- * @sflow_probability: Number of packets out of UINT_MAX to sample to the
- * %OVS_PACKET_CMD_SAMPLE multicast group, e.g. (@sflow_probability/UINT_MAX)
- * is the probability of sampling a given packet.
  *
  * Context: See the comment on locking at the top of datapath.c for additional
  * locking information.
@@ -93,9 +92,6 @@ struct datapath {
 
        /* Stats. */
        struct dp_stats_percpu __percpu *stats_percpu;
-
-       /* sFlow Sampling */
-       unsigned int sflow_probability;
 };
 
 /**
@@ -129,18 +125,12 @@ struct ovs_skb_cb {
  * struct dp_upcall - metadata to include with a packet to send to userspace
  * @cmd: One of %OVS_PACKET_CMD_*.
  * @key: Becomes %OVS_PACKET_ATTR_KEY.  Must be nonnull.
- * @userdata: Becomes %OVS_PACKET_ATTR_USERDATA if nonzero.
- * @sample_pool: Becomes %OVS_PACKET_ATTR_SAMPLE_POOL if nonzero.
- * @actions: Becomes %OVS_PACKET_ATTR_ACTIONS if nonnull.
- * @actions_len: Number of bytes in @actions.
-*/
+ * @userdata: Becomes %OVS_PACKET_ATTR_USERDATA if cmd is 
%OVS_PACKET_CMD_ACTION
+ */
 struct dp_upcall_info {
        u8 cmd;
        const struct sw_flow_key *key;
        u64 userdata;
-       u32 sample_pool;
-       const struct nlattr *actions;
-       u32 actions_len;
 };
 
 extern struct notifier_block dp_device_notifier;
diff --git a/datapath/vport.c b/datapath/vport.c
index 71fdd84..b72ff14 100644
--- a/datapath/vport.c
+++ b/datapath/vport.c
@@ -176,7 +176,6 @@ struct vport *vport_alloc(int priv_size, const struct 
vport_ops *ops, const stru
 
        vport->dp = parms->dp;
        vport->port_no = parms->port_no;
-       atomic_set(&vport->sflow_pool, 0);
        vport->ops = ops;
 
        /* Initialize kobject for bridge.  This will be added as
diff --git a/datapath/vport.h b/datapath/vport.h
index e7d2eb5..ba57f3f 100644
--- a/datapath/vport.h
+++ b/datapath/vport.h
@@ -80,8 +80,6 @@ struct vport_err_stats {
  * &struct vport.  (We keep this around so that we can delete it if the
  * device gets renamed.)  Set to the null string when no link exists.
  * @node: Element in @dp's @port_list.
- * @sflow_pool: Number of packets that were candidates for sFlow sampling,
- * regardless of whether they were actually chosen and sent down to userspace.
  * @hash_node: Element in @dev_table hash table in vport.c.
  * @ops: Class structure.
  * @percpu_stats: Points to per-CPU statistics used and maintained by vport
@@ -97,7 +95,6 @@ struct vport {
        struct kobject kobj;
        char linkname[IFNAMSIZ];
        struct list_head node;
-       atomic_t sflow_pool;
 
        struct hlist_node hash_node;
        const struct vport_ops *ops;
diff --git a/include/openvswitch/datapath-protocol.h 
b/include/openvswitch/datapath-protocol.h
index fc7cc1f..b2badb7 100644
--- a/include/openvswitch/datapath-protocol.h
+++ b/include/openvswitch/datapath-protocol.h
@@ -90,10 +90,6 @@ struct ovs_header {
  * @OVS_DP_ATTR_IPV4_FRAGS: One of %OVS_DP_FRAG_*.  Always present in
  * notifications.  May be included in %OVS_DP_NEW or %OVS_DP_SET requests to
  * change the fragment handling policy.
- * @OVS_DP_ATTR_SAMPLING: 32-bit fraction of packets to sample with
- * @OVS_PACKET_CMD_SAMPLE.  A value of 0 samples no packets, a value of
- * %UINT32_MAX samples all packets, and intermediate values sample intermediate
- * fractions of packets.
  * @OVS_DP_ATTR_MCGROUPS: Nested attributes with multicast groups.  Each nested
  * attribute has a %OVS_PACKET_CMD_* type with a 32-bit value giving the
  * Generic Netlink multicast group number used for sending this datapath's
@@ -107,7 +103,6 @@ enum ovs_datapath_attr {
        OVS_DP_ATTR_NAME,       /* name of dp_ifindex netdev */
        OVS_DP_ATTR_STATS,      /* struct ovs_dp_stats */
        OVS_DP_ATTR_IPV4_FRAGS, /* 32-bit enum ovs_frag_handling */
-       OVS_DP_ATTR_SAMPLING,   /* 32-bit fraction of packets to sample. */
        OVS_DP_ATTR_MCGROUPS,   /* Nested attributes with multicast groups. */
        __OVS_DP_ATTR_MAX
 };
@@ -157,7 +152,6 @@ enum ovs_packet_cmd {
         /* Kernel-to-user notifications. */
         OVS_PACKET_CMD_MISS,    /* Flow table miss. */
         OVS_PACKET_CMD_ACTION,  /* OVS_ACTION_ATTR_USERSPACE action. */
-        OVS_PACKET_CMD_SAMPLE,  /* Sampled packet. */
 
         /* User commands. */
         OVS_PACKET_CMD_EXECUTE  /* Apply actions to a packet. */
@@ -177,12 +171,8 @@ enum ovs_packet_cmd {
  * @OVS_PACKET_ATTR_USERDATA: Present for an %OVS_PACKET_CMD_ACTION
  * notification if the %OVS_ACTION_ATTR_USERSPACE, action's argument was
  * nonzero.
- * @OVS_PACKET_ATTR_SAMPLE_POOL: Present for %OVS_PACKET_CMD_SAMPLE.  Contains
- * the number of packets processed so far that were candidates for sampling.
- * @OVS_PACKET_ATTR_ACTIONS: Present for %OVS_PACKET_CMD_SAMPLE.  Contains a
- * copy of the actions applied to the packet, as nested %OVS_ACTION_ATTR_*
- * attributes.
- *
+ * @OVS_PACKET_ATTR_ACTIONS: Contains a copy of the actions for the packet. 
Used
+ * for %OVS_PACKET_CMD_EXECUTE. It has nested %OVS_ACTION_ATTR_* attributes.
  * These attributes follow the &struct ovs_header within the Generic Netlink
  * payload for %OVS_PACKET_* commands.
  */
@@ -191,7 +181,6 @@ enum ovs_packet_attr {
        OVS_PACKET_ATTR_PACKET,      /* Packet data. */
        OVS_PACKET_ATTR_KEY,         /* Nested OVS_KEY_ATTR_* attributes. */
        OVS_PACKET_ATTR_USERDATA,    /* u64 OVS_ACTION_ATTR_USERSPACE arg. */
-       OVS_PACKET_ATTR_SAMPLE_POOL, /* # sampling candidate packets so far. */
        OVS_PACKET_ATTR_ACTIONS,     /* Nested OVS_ACTION_ATTR_* attributes. */
        __OVS_PACKET_ATTR_MAX
 };
@@ -411,6 +400,21 @@ enum ovs_flow_attr {
 
 #define OVS_FLOW_ATTR_MAX (__OVS_FLOW_ATTR_MAX - 1)
 
+/**
+ * enum ovs_sample_attr - Attributes for ODP_ACTION_ATTR_SAMPLE
+ * @OVS_SAMPLE_ATTR_PROBABILITY: Prabability for sample event.
+ * @OVS_SAMPLE_ATTR_ACTIONS: Set of actions to execute in sample event.
+ * Actions are passed as nested attributes.
+ */
+enum ovs_sample_attr {
+       OVS_SAMPLE_ATTR_UNSPEC,
+       OVS_SAMPLE_ATTR_PROBABILITY, /* u32 number */
+       OVS_SAMPLE_ATTR_ACTIONS,     /* Nested OVS_ACTION_ATTR_* attributes. */
+       __OVS_SAMPLE_ATTR_MAX,
+};
+
+#define OVS_SAMPLE_ATTR_MAX (__OVS_SAMPLE_ATTR_MAX - 1)
+
 /* Action types. */
 enum ovs_action_type {
        OVS_ACTION_ATTR_UNSPEC,
@@ -428,6 +432,7 @@ enum ovs_action_type {
        OVS_ACTION_ATTR_SET_TUNNEL,   /* Set the encapsulating tunnel ID. */
        OVS_ACTION_ATTR_SET_PRIORITY, /* Set skb->priority. */
        OVS_ACTION_ATTR_POP_PRIORITY, /* Restore original skb->priority. */
+       ODP_ACTION_ATTR_SAMPLE,       /* SAMPLE action */
        __OVS_ACTION_ATTR_MAX
 };
 
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
index 15a21e6..2bd14e5 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -75,7 +75,6 @@ struct dpif_linux_dp {
     const char *name;                  /* OVS_DP_ATTR_NAME. */
     struct ovs_dp_stats stats;         /* OVS_DP_ATTR_STATS. */
     enum ovs_frag_handling ipv4_frags; /* OVS_DP_ATTR_IPV4_FRAGS. */
-    const uint32_t *sampling;          /* OVS_DP_ATTR_SAMPLING. */
     uint32_t mcgroups[DPIF_N_UC_TYPES]; /* OVS_DP_ATTR_MCGROUPS. */
 };
 
@@ -883,35 +882,6 @@ dpif_linux_recv_set_mask(struct dpif *dpif_, int 
listen_mask)
 }
 
 static int
-dpif_linux_get_sflow_probability(const struct dpif *dpif_,
-                                 uint32_t *probability)
-{
-    struct dpif_linux_dp dp;
-    struct ofpbuf *buf;
-    int error;
-
-    error = dpif_linux_dp_get(dpif_, &dp, &buf);
-    if (!error) {
-        *probability = dp.sampling ? *dp.sampling : 0;
-        ofpbuf_delete(buf);
-    }
-    return error;
-}
-
-static int
-dpif_linux_set_sflow_probability(struct dpif *dpif_, uint32_t probability)
-{
-    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
-    struct dpif_linux_dp dp;
-
-    dpif_linux_dp_init(&dp);
-    dp.cmd = OVS_DP_CMD_SET;
-    dp.dp_ifindex = dpif->dp_ifindex;
-    dp.sampling = &probability;
-    return dpif_linux_dp_transact(&dp, NULL, NULL);
-}
-
-static int
 dpif_linux_queue_to_priority(const struct dpif *dpif OVS_UNUSED,
                              uint32_t queue_id, uint32_t *priority)
 {
@@ -936,8 +906,6 @@ parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall 
*upcall,
         /* OVS_PACKET_CMD_ACTION only. */
         [OVS_PACKET_ATTR_USERDATA] = { .type = NL_A_U64, .optional = true },
 
-        /* OVS_PACKET_CMD_SAMPLE only. */
-        [OVS_PACKET_ATTR_SAMPLE_POOL] = { .type = NL_A_U32, .optional = true },
         [OVS_PACKET_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true },
     };
 
@@ -961,9 +929,7 @@ parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall 
*upcall,
     }
 
     type = (genl->cmd == OVS_PACKET_CMD_MISS ? DPIF_UC_MISS
-            : genl->cmd == OVS_PACKET_CMD_ACTION ? DPIF_UC_ACTION
-            : genl->cmd == OVS_PACKET_CMD_SAMPLE ? DPIF_UC_SAMPLE
-            : -1);
+            : genl->cmd == OVS_PACKET_CMD_ACTION ? DPIF_UC_ACTION : -1);
     if (type < 0) {
         return EINVAL;
     }
@@ -978,14 +944,6 @@ parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall 
*upcall,
     upcall->userdata = (a[OVS_PACKET_ATTR_USERDATA]
                         ? nl_attr_get_u64(a[OVS_PACKET_ATTR_USERDATA])
                         : 0);
-    upcall->sample_pool = (a[OVS_PACKET_ATTR_SAMPLE_POOL]
-                        ? nl_attr_get_u32(a[OVS_PACKET_ATTR_SAMPLE_POOL])
-                           : 0);
-    if (a[OVS_PACKET_ATTR_ACTIONS]) {
-        upcall->actions = (void *) nl_attr_get(a[OVS_PACKET_ATTR_ACTIONS]);
-        upcall->actions_len = nl_attr_get_size(a[OVS_PACKET_ATTR_ACTIONS]);
-    }
-
     *dp_ifindex = ovs_header->dp_ifindex;
 
     return 0;
@@ -1077,8 +1035,6 @@ const struct dpif_class dpif_linux_class = {
     dpif_linux_execute,
     dpif_linux_recv_get_mask,
     dpif_linux_recv_set_mask,
-    dpif_linux_get_sflow_probability,
-    dpif_linux_set_sflow_probability,
     dpif_linux_queue_to_priority,
     dpif_linux_recv,
     dpif_linux_recv_wait,
@@ -1390,7 +1346,6 @@ dpif_linux_dp_from_ofpbuf(struct dpif_linux_dp *dp, const 
struct ofpbuf *buf)
                                 .max_len = sizeof(struct ovs_dp_stats),
                                 .optional = true },
         [OVS_DP_ATTR_IPV4_FRAGS] = { .type = NL_A_U32, .optional = true },
-        [OVS_DP_ATTR_SAMPLING] = { .type = NL_A_U32, .optional = true },
         [OVS_DP_ATTR_MCGROUPS] = { .type = NL_A_NESTED, .optional = true },
     };
 
@@ -1425,15 +1380,11 @@ dpif_linux_dp_from_ofpbuf(struct dpif_linux_dp *dp, 
const struct ofpbuf *buf)
     if (a[OVS_DP_ATTR_IPV4_FRAGS]) {
         dp->ipv4_frags = nl_attr_get_u32(a[OVS_DP_ATTR_IPV4_FRAGS]);
     }
-    if (a[OVS_DP_ATTR_SAMPLING]) {
-        dp->sampling = nl_attr_get(a[OVS_DP_ATTR_SAMPLING]);
-    }
 
     if (a[OVS_DP_ATTR_MCGROUPS]) {
         static const struct nl_policy ovs_mcgroup_policy[] = {
             [OVS_PACKET_CMD_MISS] = { .type = NL_A_U32, .optional = true },
             [OVS_PACKET_CMD_ACTION] = { .type = NL_A_U32, .optional = true },
-            [OVS_PACKET_CMD_SAMPLE] = { .type = NL_A_U32, .optional = true },
         };
 
         struct nlattr *mcgroups[ARRAY_SIZE(ovs_mcgroup_policy)];
@@ -1451,10 +1402,6 @@ dpif_linux_dp_from_ofpbuf(struct dpif_linux_dp *dp, 
const struct ofpbuf *buf)
             dp->mcgroups[DPIF_UC_ACTION]
                 = nl_attr_get_u32(mcgroups[OVS_PACKET_CMD_ACTION]);
         }
-        if (mcgroups[OVS_PACKET_CMD_SAMPLE]) {
-            dp->mcgroups[DPIF_UC_SAMPLE]
-                = nl_attr_get_u32(mcgroups[OVS_PACKET_CMD_SAMPLE]);
-        }
     }
 
     return 0;
@@ -1481,10 +1428,6 @@ dpif_linux_dp_to_ofpbuf(const struct dpif_linux_dp *dp, 
struct ofpbuf *buf)
     if (dp->ipv4_frags) {
         nl_msg_put_u32(buf, OVS_DP_ATTR_IPV4_FRAGS, dp->ipv4_frags);
     }
-
-    if (dp->sampling) {
-        nl_msg_put_u32(buf, OVS_DP_ATTR_SAMPLING, *dp->sampling);
-    }
 }
 
 /* Clears 'dp' to "empty" values. */
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 359c80b..926464e 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1379,8 +1379,6 @@ const struct dpif_class dpif_netdev_class = {
     dpif_netdev_execute,
     dpif_netdev_recv_get_mask,
     dpif_netdev_recv_set_mask,
-    NULL,                       /* get_sflow_probability */
-    NULL,                       /* set_sflow_probability */
     NULL,                       /* queue_to_priority */
     dpif_netdev_recv,
     dpif_netdev_recv_wait,
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
index c6c39da..558b891 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -303,25 +303,6 @@ struct dpif_class {
      * called. */
     int (*recv_set_mask)(struct dpif *dpif, int listen_mask);
 
-    /* Retrieves 'dpif''s sFlow sampling probability into '*probability'.
-     * Return value is 0 or a positive errno value.  EOPNOTSUPP indicates that
-     * the datapath does not support sFlow, as does a null pointer.
-     *
-     * '*probability' is expressed as the number of packets out of UINT_MAX to
-     * sample, e.g. probability/UINT_MAX is the probability of sampling a given
-     * packet. */
-    int (*get_sflow_probability)(const struct dpif *dpif,
-                                 uint32_t *probability);
-
-    /* Sets 'dpif''s sFlow sampling probability to 'probability'.  Return value
-     * is 0 or a positive errno value.  EOPNOTSUPP indicates that the datapath
-     * does not support sFlow, as does a null pointer.
-     *
-     * 'probability' is expressed as the number of packets out of UINT_MAX to
-     * sample, e.g. probability/UINT_MAX is the probability of sampling a given
-     * packet. */
-    int (*set_sflow_probability)(struct dpif *dpif, uint32_t probability);
-
     /* Translates OpenFlow queue ID 'queue_id' (in host byte order) into a
      * priority value for use in the OVS_ACTION_ATTR_SET_PRIORITY action in
      * '*priority'. */
diff --git a/lib/dpif.c b/lib/dpif.c
index ad143c8..2d21a9f 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -958,7 +958,6 @@ dpif_upcall_type_to_string(enum dpif_upcall_type type)
     switch (type) {
     case DPIF_UC_MISS: return "miss";
     case DPIF_UC_ACTION: return "action";
-    case DPIF_UC_SAMPLE: return "sample";
     case DPIF_N_UC_TYPES: default: return "<unknown>";
     }
 }
@@ -967,8 +966,7 @@ static bool OVS_UNUSED
 is_valid_listen_mask(int listen_mask)
 {
     return !(listen_mask & ~((1u << DPIF_UC_MISS) |
-                             (1u << DPIF_UC_ACTION) |
-                             (1u << DPIF_UC_SAMPLE)));
+                             (1u << DPIF_UC_ACTION)));
 }
 
 /* Retrieves 'dpif''s "listen mask" into '*listen_mask'.  A 1-bit of value 2**X
@@ -1003,41 +1001,6 @@ dpif_recv_set_mask(struct dpif *dpif, int listen_mask)
     return error;
 }
 
-/* Retrieve the sFlow sampling probability.  '*probability' is expressed as the
- * number of packets out of UINT_MAX to sample, e.g. probability/UINT_MAX is
- * the probability of sampling a given packet.
- *
- * Returns 0 if successful, otherwise a positive errno value.  EOPNOTSUPP
- * indicates that 'dpif' does not support sFlow sampling. */
-int
-dpif_get_sflow_probability(const struct dpif *dpif, uint32_t *probability)
-{
-    int error = (dpif->dpif_class->get_sflow_probability
-                 ? dpif->dpif_class->get_sflow_probability(dpif, probability)
-                 : EOPNOTSUPP);
-    if (error) {
-        *probability = 0;
-    }
-    log_operation(dpif, "get_sflow_probability", error);
-    return error;
-}
-
-/* Set the sFlow sampling probability.  'probability' is expressed as the
- * number of packets out of UINT_MAX to sample, e.g. probability/UINT_MAX is
- * the probability of sampling a given packet.
- *
- * Returns 0 if successful, otherwise a positive errno value.  EOPNOTSUPP
- * indicates that 'dpif' does not support sFlow sampling. */
-int
-dpif_set_sflow_probability(struct dpif *dpif, uint32_t probability)
-{
-    int error = (dpif->dpif_class->set_sflow_probability
-                 ? dpif->dpif_class->set_sflow_probability(dpif, probability)
-                 : EOPNOTSUPP);
-    log_operation(dpif, "set_sflow_probability", error);
-    return error;
-}
-
 /* Polls for an upcall from 'dpif'.  If successful, stores the upcall into
  * '*upcall'.  Only upcalls of the types selected with dpif_recv_set_mask()
  * member function will ordinarily be received (but if a message type is
diff --git a/lib/dpif.h b/lib/dpif.h
index c01010d..b572d0f 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -155,7 +155,6 @@ int dpif_execute(struct dpif *,
 enum dpif_upcall_type {
     DPIF_UC_MISS,               /* Miss in flow table. */
     DPIF_UC_ACTION,             /* OVS_ACTION_ATTR_USERSPACE action. */
-    DPIF_UC_SAMPLE,             /* Packet sampling. */
     DPIF_N_UC_TYPES
 };
 
@@ -177,17 +176,10 @@ struct dpif_upcall {
 
     /* DPIF_UC_ACTION only. */
     uint64_t userdata;          /* Argument to OVS_ACTION_ATTR_USERSPACE. */
-
-    /* DPIF_UC_SAMPLE only. */
-    uint32_t sample_pool;       /* # of sampling candidate packets so far. */
-    struct nlattr *actions;     /* Associated flow actions. */
-    size_t actions_len;
 };
 
 int dpif_recv_get_mask(const struct dpif *, int *listen_mask);
 int dpif_recv_set_mask(struct dpif *, int listen_mask);
-int dpif_get_sflow_probability(const struct dpif *, uint32_t *probability);
-int dpif_set_sflow_probability(struct dpif *, uint32_t probability);
 int dpif_recv(struct dpif *, struct dpif_upcall *);
 void dpif_recv_purge(struct dpif *);
 void dpif_recv_wait(struct dpif *);
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 3d94398..fd2b24f 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -32,6 +32,7 @@
 #include "packets.h"
 #include "timeval.h"
 #include "util.h"
+#include "ofproto/ofproto.h"
 
 /* The interface between userspace and kernel uses an "OVS_*" prefix.
  * Since this is fairly non-specific for the OVS userspace components,
@@ -61,6 +62,7 @@ odp_action_len(uint16_t type)
     case OVS_ACTION_ATTR_SET_TUNNEL: return 8;
     case OVS_ACTION_ATTR_SET_PRIORITY: return 4;
     case OVS_ACTION_ATTR_POP_PRIORITY: return 0;
+    case ODP_ACTION_ATTR_SAMPLE: return 24;
 
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
@@ -89,11 +91,48 @@ format_generic_odp_action(struct ds *ds, const struct 
nlattr *a)
     }
 }
 
+static void format_odp_sample_action(struct ds *ds, const struct nlattr *attr)
+{
+    static const struct nl_policy ovs_sample_act[] = {
+        [OVS_SAMPLE_ATTR_PROBABILITY] = { .type = NL_A_U32, .optional = false 
},
+        [OVS_SAMPLE_ATTR_ACTIONS] = { .type = NL_A_UNSPEC, .optional = true },
+    };
+    struct nlattr *a[ARRAY_SIZE(ovs_sample_act)];
+    struct ofpbuf buf;
+    uint32_t percentage;
+
+    ofpbuf_use_const(&buf, nl_attr_get(attr), nl_attr_get_size(attr));
+    ds_put_cstr(ds, "sample");
+    if (!nl_policy_parse(&buf, 0, ovs_sample_act, a,
+                            ARRAY_SIZE(ovs_sample_act))) {
+        ds_put_cstr(ds, "(error)");
+        return;
+    }
+
+    percentage = 100 /
+                (UINT32_MAX / nl_attr_get_u32(a[OVS_SAMPLE_ATTR_PROBABILITY]));
+
+    ds_put_format(ds, "(sample=%"PRIu32"%%", percentage);
+
+    if (a[OVS_SAMPLE_ATTR_ACTIONS]) {
+        const struct nlattr *nla_acts = 
nl_attr_get(a[OVS_SAMPLE_ATTR_ACTIONS]);
+        int len = nl_attr_get_size(a[OVS_SAMPLE_ATTR_ACTIONS]);
+
+        ds_put_cstr(ds, ",actions(");
+        format_odp_actions(ds, nla_acts, len);
+        ds_put_format(ds, ")");
+
+    }
+    ds_put_format(ds, ")");
+}
+
 void
 format_odp_action(struct ds *ds, const struct nlattr *a)
 {
     const uint8_t *eth;
     ovs_be32 ip;
+    struct user_data userdata;
+    uint64_t data;
 
     if (nl_attr_get_size(a) != odp_action_len(nl_attr_type(a))) {
         ds_put_format(ds, "bad length %zu, expected %d for: ",
@@ -107,7 +146,19 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
         ds_put_format(ds, "%"PRIu16, nl_attr_get_u32(a));
         break;
     case OVS_ACTION_ATTR_USERSPACE:
-        ds_put_format(ds, "userspace(%"PRIu64")", nl_attr_get_u64(a));
+        data = nl_attr_get_u64(a);
+        memcpy(&userdata, &data, sizeof(userdata));
+        if (userdata.type == OVS_USERDATA_CONTROLLER)
+                ds_put_format(ds, "userspace(controller,data=%"PRIu32")",
+                       userdata.controller_data);
+        else if (userdata.type == OVS_USERDATA_SFLOW)
+                ds_put_format(ds, "userspace(sFlow,n_output=%"PRIu8","
+                    "vid=%"PRIu16",pcp=%"PRIu8",ifindix=%"PRIu32")",
+                    userdata.n_output, userdata.vlan_id, userdata.vlan_pcp,
+                    userdata.port_ifindex);
+        else
+                ds_put_format(ds, "userspace(Unknown,data=%"PRIu64")",
+                                            nl_attr_get_u64(a));
         break;
     case OVS_ACTION_ATTR_SET_TUNNEL:
         ds_put_format(ds, "set_tunnel(%#"PRIx64")",
@@ -152,6 +203,9 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
     case OVS_ACTION_ATTR_POP_PRIORITY:
         ds_put_cstr(ds, "pop_priority");
         break;
+    case ODP_ACTION_ATTR_SAMPLE:
+        format_odp_sample_action(ds, a);
+        break;
     default:
         format_generic_odp_action(ds, a);
         break;
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index 68eb804..6cf49f9 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -46,7 +46,6 @@ struct dpif_sflow_port {
 };
 
 struct dpif_sflow {
-    struct ofproto *ofproto;
     struct collectors *collectors;
     SFLAgent *sflow_agent;
     struct ofproto_sflow_options *options;
@@ -54,6 +53,7 @@ struct dpif_sflow {
     time_t next_tick;
     size_t n_flood, n_all;
     struct hmap ports;          /* Contains "struct dpif_sflow_port"s. */
+    uint32_t probability;
 };
 
 static void dpif_sflow_del_port__(struct dpif_sflow *,
@@ -268,9 +268,6 @@ dpif_sflow_clear(struct dpif_sflow *ds)
     ds->collectors = NULL;
     ofproto_sflow_options_destroy(ds->options);
     ds->options = NULL;
-
-    /* Turn off sampling to save CPU cycles. */
-    dpif_set_sflow_probability(ds->dpif, 0);
 }
 
 bool
@@ -291,6 +288,11 @@ dpif_sflow_create(struct dpif *dpif)
     return ds;
 }
 
+int dpif_sflow_get_probability(const struct dpif_sflow *ds)
+{
+    return ds->probability;
+}
+
 void
 dpif_sflow_destroy(struct dpif_sflow *ds)
 {
@@ -321,6 +323,7 @@ static void
 dpif_sflow_add_sampler(struct dpif_sflow *ds, struct dpif_sflow_port *dsp)
 {
     SFLSampler *sampler = sfl_agent_addSampler(ds->sflow_agent, &dsp->dsi);
+    ds->probability = MAX(1, UINT32_MAX / ds->options->sampling_rate);
     sfl_sampler_set_sFlowFsPacketSamplingRate(sampler, 
ds->options->sampling_rate);
     sfl_sampler_set_sFlowFsMaximumHeaderSize(sampler, ds->options->header_len);
     sfl_sampler_set_sFlowFsReceiver(sampler, RECEIVER_INDEX);
@@ -456,10 +459,6 @@ dpif_sflow_set_options(struct dpif_sflow *ds,
     sfl_receiver_set_sFlowRcvrOwner(receiver, "Open vSwitch sFlow");
     sfl_receiver_set_sFlowRcvrTimeout(receiver, 0xffffffff);
 
-    /* Set the sampling_rate down in the datapath. */
-    dpif_set_sflow_probability(ds->dpif,
-                               MAX(1, UINT32_MAX / options->sampling_rate));
-
     /* Add samplers and pollers for the currently known ports. */
     HMAP_FOR_EACH (dsp, hmap_node, &ds->ports) {
         dpif_sflow_add_poller(ds, dsp, dsp->odp_port);
@@ -467,7 +466,7 @@ dpif_sflow_set_options(struct dpif_sflow *ds,
     }
 }
 
-static int
+int
 dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *ds,
                                uint16_t odp_port)
 {
@@ -476,24 +475,35 @@ dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow 
*ds,
 }
 
 void
-dpif_sflow_received(struct dpif_sflow *ds, const struct dpif_upcall *upcall,
-                    const struct flow *flow)
+dpif_sflow_received(struct dpif_sflow *ds, struct ofpbuf *packet,
+                    const struct flow *flow, const struct user_data *data)
 {
     SFL_FLOW_SAMPLE_TYPE fs;
     SFLFlow_sample_element hdrElem;
     SFLSampled_header *header;
     SFLFlow_sample_element switchElem;
     SFLSampler *sampler;
-    unsigned int left;
-    struct nlattr *a;
-    size_t n_outputs;
+    struct dpif_sflow_port *in_dsp;
+    struct netdev_stats stats;
+    int error;
 
     /* Build a flow sample */
     memset(&fs, 0, sizeof fs);
-    fs.input = dpif_sflow_odp_port_to_ifindex(ds,
-                                ofp_port_to_odp_port(flow->in_port));
-    fs.output = 0;              /* Filled in correctly below. */
-    fs.sample_pool = upcall->sample_pool;
+
+    in_dsp = dpif_sflow_find_port(ds, ofp_port_to_odp_port(flow->in_port));
+    if (!in_dsp) {
+        VLOG_WARN_RL(&rl, "No sFlow port for input port (%"PRIu32")",
+                     flow->in_port);
+        return;
+    }
+    fs.input = SFL_DS_INDEX(in_dsp->dsi);
+
+    error = netdev_get_stats(in_dsp->netdev, &stats);
+    if (error) {
+        VLOG_WARN_RL(&rl, "netdev get-stats error %s", strerror(error));
+        return;
+    }
+    fs.sample_pool = stats.rx_packets + stats.tx_packets;
 
     /* We are going to give it to the sampler that represents this input port.
      * By implementing "ingress-only" sampling like this we ensure that we
@@ -512,12 +522,12 @@ dpif_sflow_received(struct dpif_sflow *ds, const struct 
dpif_upcall *upcall,
     header->header_protocol = SFLHEADER_ETHERNET_ISO8023;
     /* The frame_length should include the Ethernet FCS (4 bytes),
        but it has already been stripped,  so we need to add 4 here. */
-    header->frame_length = upcall->packet->size + 4;
+    header->frame_length = packet->size + 4;
     /* Ethernet FCS stripped off. */
     header->stripped = 4;
-    header->header_length = MIN(upcall->packet->size,
+    header->header_length = MIN(packet->size,
                                 sampler->sFlowFsMaximumHeaderSize);
-    header->header_bytes = upcall->packet->data;
+    header->header_bytes = packet->data;
 
     /* Add extended switch element. */
     memset(&switchElem, 0, sizeof(switchElem));
@@ -529,37 +539,21 @@ dpif_sflow_received(struct dpif_sflow *ds, const struct 
dpif_upcall *upcall,
     switchElem.flowType.sw.dst_vlan = switchElem.flowType.sw.src_vlan;
     switchElem.flowType.sw.dst_priority = switchElem.flowType.sw.src_priority;
 
-    /* Figure out the output ports. */
-    n_outputs = 0;
-    NL_ATTR_FOR_EACH_UNSAFE (a, left, upcall->actions, upcall->actions_len) {
-        ovs_be16 tci;
-
-        switch (nl_attr_type(a)) {
-        case OVS_ACTION_ATTR_OUTPUT:
-            fs.output = dpif_sflow_odp_port_to_ifindex(ds, nl_attr_get_u32(a));
-            n_outputs++;
-            break;
-
-        case OVS_ACTION_ATTR_PUSH_VLAN:
-            tci = nl_attr_get_be16(a);
-            switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(tci);
-            switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(tci);
-            break;
-
-        default:
-            break;
-        }
-    }
+    /* retrieve data from user_data */
+    switchElem.flowType.sw.dst_vlan = data->vlan_id;
+    switchElem.flowType.sw.dst_priority = data->vlan_pcp;
+
+    fs.output = data->port_ifindex;
 
     /* Set output port, as defined by http://www.sflow.org/sflow_version_5.txt
        (search for "Input/output port information"). */
-    if (!n_outputs) {
+    if (!data->n_output) {
         /* This value indicates that the packet was dropped for an unknown
          * reason. */
         fs.output = 0x40000000 | 256;
-    } else if (n_outputs > 1 || !fs.output) {
+    } else if (data->n_output > 1 || !fs.output) {
         /* Setting the high bit means "multiple output ports". */
-        fs.output = 0x80000000 | n_outputs;
+        fs.output = 0x80000000 | data->n_output;
     }
 
     /* Submit the flow sample to be encoded into the next datagram. */
diff --git a/ofproto/ofproto-dpif-sflow.h b/ofproto/ofproto-dpif-sflow.h
index c7c8b5a..ff5cc38 100644
--- a/ofproto/ofproto-dpif-sflow.h
+++ b/ofproto/ofproto-dpif-sflow.h
@@ -20,6 +20,7 @@
 
 #include <stdint.h>
 #include "svec.h"
+#include "ofproto.h"
 
 struct dpif;
 struct dpif_upcall;
@@ -27,6 +28,8 @@ struct flow;
 struct ofproto_sflow_options;
 
 struct dpif_sflow *dpif_sflow_create(struct dpif *);
+int dpif_sflow_get_probability(const struct dpif_sflow *);
+
 void dpif_sflow_destroy(struct dpif_sflow *);
 void dpif_sflow_set_options(struct dpif_sflow *,
                             const struct ofproto_sflow_options *);
@@ -40,7 +43,12 @@ void dpif_sflow_del_port(struct dpif_sflow *, uint16_t 
ovs_port);
 void dpif_sflow_run(struct dpif_sflow *);
 void dpif_sflow_wait(struct dpif_sflow *);
 
-void dpif_sflow_received(struct dpif_sflow *, const struct dpif_upcall *,
-                         const struct flow *);
+void dpif_sflow_received(struct dpif_sflow *ds,
+                         struct ofpbuf *packet,
+                         const struct flow *flow,
+                         const struct user_data *data);
+int
+dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *ds,
+                               uint16_t odp_port);
 
 #endif /* ofproto/ofproto-dpif-sflow.h */
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index bd976f7..e03a0fc 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -204,6 +204,9 @@ struct action_xlate_ctx {
     struct flow base_flow;      /* Flow at the last commit. */
     uint32_t base_priority;     /* Priority at the last commit. */
     uint8_t table_id;           /* OpenFlow table ID where flow was found. */
+    uint8_t sflow_n_outputs;    /* Number of output ports */
+    bool sflow_action_done;
+    uint16_t sflow_odp_port;    /* Output port for composing sFlow action */
 };
 
 static void action_xlate_ctx_init(struct action_xlate_ctx *,
@@ -400,7 +403,10 @@ static int expire(struct ofproto_dpif *);
 /* Utilities. */
 static int send_packet(struct ofproto_dpif *, uint32_t odp_port,
                        const struct ofpbuf *packet);
-
+static void compose_sflow_action(struct ofpbuf *odp_actions,
+                                 uint32_t probability,
+                                 enum ovs_userdata_type,
+                                 uint32_t odp_port);
 /* Global variables. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
@@ -467,8 +473,7 @@ construct(struct ofproto *ofproto_, int *n_tablesp)
 
     error = dpif_recv_set_mask(ofproto->dpif,
                                ((1u << DPIF_UC_MISS) |
-                                (1u << DPIF_UC_ACTION) |
-                                (1u << DPIF_UC_SAMPLE)));
+                                (1u << DPIF_UC_ACTION)));
     if (error) {
         VLOG_ERR("failed to listen on datapath %s: %s", name, strerror(error));
         dpif_close(ofproto->dpif);
@@ -808,6 +813,7 @@ set_sflow(struct ofproto *ofproto_,
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct dpif_sflow *ds = ofproto->sflow;
+
     if (sflow_options) {
         if (!ds) {
             struct ofport_dpif *ofport;
@@ -817,11 +823,15 @@ set_sflow(struct ofproto *ofproto_,
                 dpif_sflow_add_port(ds, ofport->odp_port,
                                     netdev_get_name(ofport->up.netdev));
             }
+            ofproto->need_revalidate = true;
         }
         dpif_sflow_set_options(ds, sflow_options);
     } else {
         dpif_sflow_destroy(ds);
-        ofproto->sflow = NULL;
+        if (ds) {
+            ofproto->need_revalidate = true;
+            ofproto->sflow = NULL;
+        }
     }
     return 0;
 }
@@ -1663,12 +1673,15 @@ send_packet_in(struct ofproto_dpif *ofproto, struct 
dpif_upcall *upcall,
                const struct flow *flow, bool clone)
 {
     struct ofputil_packet_in pin;
+    struct user_data data;
+
+    memcpy(&data, &upcall->userdata, sizeof(data));
 
     pin.packet = upcall->packet;
     pin.in_port = flow->in_port;
     pin.reason = upcall->type == DPIF_UC_MISS ? OFPR_NO_MATCH : OFPR_ACTION;
     pin.buffer_id = 0;          /* not yet known */
-    pin.send_len = upcall->userdata;
+    pin.send_len = data.controller_data;
     connmgr_send_packet_in(ofproto->up.connmgr, &pin, flow,
                            clone ? NULL : upcall->packet);
 }
@@ -1772,23 +1785,36 @@ handle_miss_upcall(struct ofproto_dpif *ofproto, struct 
dpif_upcall *upcall)
 }
 
 static void
-handle_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall)
+handle_userspace_upcall(struct ofproto_dpif *ofproto,
+                        struct dpif_upcall *upcall)
 {
     struct flow flow;
+    struct user_data data;
 
-    switch (upcall->type) {
-    case DPIF_UC_ACTION:
-        COVERAGE_INC(ofproto_dpif_ctlr_action);
-        odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
-        send_packet_in(ofproto, upcall, &flow, false);
-        break;
+    memcpy(&data, &upcall->userdata, sizeof(data));
 
-    case DPIF_UC_SAMPLE:
+    if (data.type == OVS_USERDATA_SFLOW) {
         if (ofproto->sflow) {
             odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
-            dpif_sflow_received(ofproto->sflow, upcall, &flow);
+            dpif_sflow_received(ofproto->sflow, upcall->packet, &flow, &data);
         }
         ofpbuf_delete(upcall->packet);
+
+    } else if (data.type == OVS_USERDATA_CONTROLLER) {
+        COVERAGE_INC(ofproto_dpif_ctlr_action);
+        odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
+        send_packet_in(ofproto, upcall, &flow, false);
+    } else {
+        VLOG_WARN_RL(&rl, "invalid user data : %"PRIu64, upcall->userdata);
+    }
+}
+
+static void
+handle_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall)
+{
+    switch (upcall->type) {
+    case DPIF_UC_ACTION:
+        handle_userspace_upcall(ofproto, upcall);
         break;
 
     case DPIF_UC_MISS:
@@ -2136,9 +2162,6 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const 
struct flow *flow,
         upcall.key = NULL;
         upcall.key_len = 0;
         upcall.userdata = nl_attr_get_u64(odp_actions);
-        upcall.sample_pool = 0;
-        upcall.actions = NULL;
-        upcall.actions_len = 0;
 
         send_packet_in(ofproto, &upcall, flow, false);
 
@@ -2866,6 +2889,18 @@ send_packet(struct ofproto_dpif *ofproto, uint32_t 
odp_port,
     odp_flow_key_from_flow(&key, &flow);
 
     ofpbuf_init(&odp_actions, 32);
+    if (ofproto->sflow) {
+        uint32_t port_ifindex;
+        uint32_t probability;
+
+        probability = dpif_sflow_get_probability(ofproto->sflow);
+        port_ifindex = dpif_sflow_odp_port_to_ifindex(ofproto->sflow,
+                                                      odp_port);
+
+        compose_sflow_action(&odp_actions, probability,
+                             OVS_USERDATA_SFLOW, port_ifindex);
+    }
+
     nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
     error = dpif_execute(ofproto->dpif,
                          key.data, key.size,
@@ -2886,6 +2921,97 @@ static void do_xlate_actions(const union ofp_action *in, 
size_t n_in,
                              struct action_xlate_ctx *ctx);
 static void xlate_normal(struct action_xlate_ctx *);
 
+/* Compose SAMPLE action for sFlow */
+static void compose_sflow_action(struct ofpbuf *odp_actions,
+                                 uint32_t probability,
+                                 enum ovs_userdata_type type,
+                                 uint32_t port_ifindex)
+{
+    struct user_data data;
+    size_t sample_offset, actions_offset;
+
+    sample_offset = nl_msg_start_nested(odp_actions, ODP_ACTION_ATTR_SAMPLE);
+
+    nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
+
+    actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
+
+    data.type = type;
+    if (type == OVS_USERDATA_SFLOW) {
+        data.port_ifindex = port_ifindex;
+        data.n_output = 1;
+    } else {
+        data.n_output = 0;
+        data.port_ifindex = 0;
+    }
+    data.vlan_id = 0;
+    data.vlan_pcp = 0;
+
+    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_USERSPACE, &data,
+                                                           sizeof(data));
+
+    nl_msg_end_nested(odp_actions, actions_offset);
+    nl_msg_end_nested(odp_actions, sample_offset);
+}
+
+/* SAMPLE action must be first action in any given list of actions.
+ * At this point we do not have all information required to build it. So try to
+ * build sample action as complete as possible. */
+static void
+add_sflow_action(struct action_xlate_ctx *ctx)
+{
+    uint32_t probability;
+
+    if (!ctx->ofproto->sflow || ctx->sflow_action_done)
+        return;
+
+    probability = dpif_sflow_get_probability(ctx->ofproto->sflow);
+    compose_sflow_action(ctx->odp_actions, probability,
+                         OVS_USERDATA_UNSPEC, 0);
+
+    ctx->sflow_odp_port = 0;
+    ctx->sflow_n_outputs = 0;
+}
+
+/* Fix SAMPLE action according to data collected while composing actions.
+ * Specifically we need to fix SAMPLE actions OVS_SAMPLE_ATTR_ACTIONS attribute
+ * i.e. nested USERSPACE action's userdata which is required for sflow. */
+static void
+fix_sflow_action(struct action_xlate_ctx *ctx)
+{
+    const struct flow *base = &ctx->base_flow;
+    struct nlattr *nla, *userdata, *nla_acts;
+    struct user_data *data;
+
+    if (!ctx->ofproto->sflow || ctx->sflow_action_done)
+        return;
+
+    nla = (struct nlattr *) nl_attr_find(ctx->odp_actions, 0,
+                                              ODP_ACTION_ATTR_SAMPLE);
+    assert(nla != NULL);
+
+    nla_acts = (struct nlattr *)nl_attr_find_nested(nla,
+                                              OVS_SAMPLE_ATTR_ACTIONS);
+    assert(nla_acts != NULL);
+
+    userdata = (struct nlattr *)nl_attr_find_nested(nla_acts,
+                                              OVS_ACTION_ATTR_USERSPACE);
+    assert(userdata != NULL);
+
+    data = (struct user_data *) nl_attr_get_unspec(userdata, sizeof(*data));
+    assert(data != NULL);
+
+    data->type = OVS_USERDATA_SFLOW;
+    if (ctx->sflow_n_outputs) {
+        data->port_ifindex = 
dpif_sflow_odp_port_to_ifindex(ctx->ofproto->sflow,
+                                          ctx->sflow_odp_port);
+    }
+    data->n_output = ctx->sflow_n_outputs;
+    data->vlan_id = vlan_tci_to_vid(base->vlan_tci);
+    data->vlan_pcp = vlan_tci_to_pcp(base->vlan_tci);
+    ctx->sflow_action_done = true;
+}
+
 static void
 commit_odp_actions(struct action_xlate_ctx *ctx)
 {
@@ -2893,6 +3019,8 @@ commit_odp_actions(struct action_xlate_ctx *ctx)
     struct flow *base = &ctx->base_flow;
     struct ofpbuf *odp_actions = ctx->odp_actions;
 
+    add_sflow_action(ctx);
+
     if (base->tun_id != flow->tun_id) {
         nl_msg_put_be64(odp_actions, OVS_ACTION_ATTR_SET_TUNNEL, flow->tun_id);
         base->tun_id = flow->tun_id;
@@ -2957,6 +3085,8 @@ commit_odp_actions(struct action_xlate_ctx *ctx)
         }
         ctx->base_priority = ctx->priority;
     }
+
+    fix_sflow_action(ctx);
 }
 
 static void
@@ -2981,6 +3111,8 @@ add_output_action(struct action_xlate_ctx *ctx, uint16_t 
ofp_port)
     commit_odp_actions(ctx);
     nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
     ctx->nf_output_iface = ofp_port;
+    ctx->sflow_odp_port = odp_port;
+    ctx->sflow_n_outputs++;
 }
 
 static void
@@ -3062,12 +3194,24 @@ flood_packets(struct action_xlate_ctx *ctx, ovs_be32 
mask)
         if (ofp_port != ctx->flow.in_port && !(ofport->up.opp.config & mask)) {
             nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT,
                            ofport->odp_port);
+            ctx->sflow_odp_port = ofport->odp_port;
+            ctx->sflow_n_outputs++;
         }
     }
 
     ctx->nf_output_iface = NF_OUT_FLOOD;
 }
 
+static void compose_controller_action(struct ofpbuf *odp_actions, int len)
+{
+    struct user_data data;
+
+    data.type = OVS_USERDATA_CONTROLLER;
+    data.controller_data = len;
+    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_USERSPACE,
+                                       &data, sizeof data);
+}
+
 static void
 xlate_output_action__(struct action_xlate_ctx *ctx,
                       uint16_t port, uint16_t max_len)
@@ -3094,7 +3238,7 @@ xlate_output_action__(struct action_xlate_ctx *ctx,
         break;
     case OFPP_CONTROLLER:
         commit_odp_actions(ctx);
-        nl_msg_put_u64(ctx->odp_actions, OVS_ACTION_ATTR_USERSPACE, max_len);
+        compose_controller_action(ctx->odp_actions, max_len);
         break;
     case OFPP_LOCAL:
         add_output_action(ctx, OFPP_LOCAL);
@@ -3438,6 +3582,7 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx,
     ctx->packet = packet;
     ctx->may_learn = packet != NULL;
     ctx->resubmit_hook = NULL;
+    ctx->sflow_action_done = false;
 }
 
 static struct ofpbuf *
@@ -3742,6 +3887,7 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t 
vlan,
     compose_mirror_dsts(ctx, vlan, in_bundle, &set);
 
     /* Output all the packets we can without having to change the VLAN. */
+    add_sflow_action(ctx);
     initial_vlan = vlan_tci_to_vid(ctx->flow.vlan_tci);
     if (initial_vlan == 0) {
         initial_vlan = OFP_VLAN_NONE;
@@ -3752,19 +3898,25 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t 
vlan,
         }
         nl_msg_put_u32(ctx->odp_actions,
                        OVS_ACTION_ATTR_OUTPUT, dst->port->odp_port);
+        ctx->sflow_odp_port = dst->port->odp_port;
+        ctx->sflow_n_outputs++;
     }
 
     /* Then output the rest. */
     cur_vlan = initial_vlan;
     for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) {
         if (dst->vlan == initial_vlan) {
+            /* Used by sFlow */
+            ctx->base_flow.vlan_tci = ctx->flow.vlan_tci;
             continue;
         }
         if (dst->vlan != cur_vlan) {
+            ovs_be16 tci;
+
             if (dst->vlan == OFP_VLAN_NONE) {
                 nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_VLAN);
+                tci = htons(0);
             } else {
-                ovs_be16 tci;
 
                 if (cur_vlan != OFP_VLAN_NONE) {
                      nl_msg_put_flag(ctx->odp_actions, 
OVS_ACTION_ATTR_POP_VLAN);
@@ -3775,11 +3927,16 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t 
vlan,
                                 OVS_ACTION_ATTR_PUSH_VLAN, tci);
             }
             cur_vlan = dst->vlan;
+            ctx->base_flow.vlan_tci = tci;
         }
         nl_msg_put_u32(ctx->odp_actions,
                        OVS_ACTION_ATTR_OUTPUT, dst->port->odp_port);
+
+        ctx->sflow_odp_port = dst->port->odp_port;
+        ctx->sflow_n_outputs++;
     }
 
+    fix_sflow_action(ctx);
     dst_set_free(&set);
 }
 
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 3552aac..78d2535 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -243,6 +243,26 @@ int ofproto_port_get_cfm_remote_mpids(const struct ofproto 
*,
 void ofproto_get_ofproto_controller_info(const struct ofproto *, struct shash 
*);
 void ofproto_free_ofproto_controller_info(struct shash *);
 
+enum ovs_userdata_type {
+        OVS_USERDATA_UNSPEC,
+        OVS_USERDATA_CONTROLLER,   /* packet for controller */
+        OVS_USERDATA_SFLOW,        /* packet for sFlow sample packet*/
+};
+
+/* user_data is passed as argument to OVS_ACTION_ATTR_USERSPACE
+ * Since is it passed to kernel as u64, its size has to be 8 bytes */
+struct user_data {
+    uint8_t   type;                 /* enum ovs_userdata_type */
+    uint8_t   n_output;             /* no of output ports. used by sflow */
+    uint16_t  vlan_id:13;           /* used by sFlow */
+    uint16_t  vlan_pcp:3;           /* used by sFlow */
+    union {
+        uint32_t controller_data;   /* data for OFPP_CONTROLLER */
+        uint32_t port_ifindex;      /* used by sFlow */
+    };
+};
+
+BUILD_ASSERT_DECL(sizeof(struct user_data) == 8);
 #ifdef  __cplusplus
 }
 #endif
-- 
1.7.1

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to