diff mbox

[ovs-dev,PATCHv6,2/3] ofproto-dpif-sflow: Add snaplen for sample action and sFlow.

Message ID 1465365234-59223-3-git-send-email-u9012063@gmail.com
State Changes Requested
Headers show

Commit Message

William Tu June 8, 2016, 5:53 a.m. UTC
This patch adds a 'snaplen' field in sample action.  Currently, sample
action is used by sFlow and IPFIX. For IPFIX, nothing is changed.  For
sFlow configuration, the patch translates header=N to a sample action with
snaplen=N, then the snaplen=N translates to trunc(N) in kernel datapath.
Thus, only N bytes instead of full-packet size will be copied from kernel
to userspace, saving the copying overhead.

Signed-off-by: William Tu <u9012063@gmail.com>
---
 include/openvswitch/ofp-actions.h |  1 +
 lib/ofp-actions.c                 | 10 ++++++++-
 ofproto/ofproto-dpif-sflow.c      | 19 ++++++++++++++---
 ofproto/ofproto-dpif-sflow.h      |  2 ++
 ofproto/ofproto-dpif-upcall.c     |  4 +++-
 ofproto/ofproto-dpif-xlate.c      | 25 ++++++++++++++++++++--
 tests/odp.at                      |  2 ++
 tests/ofp-actions.at              |  8 +++----
 tests/ofproto-dpif.at             | 17 +++++++++++++++
 tests/ovs-ofctl.at                | 24 ++++++++++-----------
 tests/system-traffic.at           | 45 +++++++++++++++++++++++++++++++++++++++
 tests/system-userspace-macros.at  |  9 ++++++++
 12 files changed, 143 insertions(+), 23 deletions(-)

Comments

Ben Pfaff June 9, 2016, 11:59 p.m. UTC | #1
On Tue, Jun 07, 2016 at 10:53:53PM -0700, William Tu wrote:
> This patch adds a 'snaplen' field in sample action.  Currently, sample
> action is used by sFlow and IPFIX. For IPFIX, nothing is changed.  For
> sFlow configuration, the patch translates header=N to a sample action with
> snaplen=N, then the snaplen=N translates to trunc(N) in kernel datapath.
> Thus, only N bytes instead of full-packet size will be copied from kernel
> to userspace, saving the copying overhead.
> 
> Signed-off-by: William Tu <u9012063@gmail.com>

This changes the "wire format" of the NXAST_SAMPLE action, but we can't
do that because it will break compatibility with older OpenFlow clients.
Usually if we need a new feature in an action we add a new version that
has that feature, e.g. NXAST_SAMPLE2.  The IPFIX enhancements that Benli
Ye are working on already do that for another reason, so you should
probably coordinate so that we don't end up with three versions of the
sample action.

I'm not sure why CHECK_TRUNC_USERSPACE exists, because I think that your
patch implements the new action in userspace.
William Tu June 10, 2016, 1:06 a.m. UTC | #2
Hi Ben,
Thanks for the review.

> This changes the "wire format" of the NXAST_SAMPLE action, but we can't
> do that because it will break compatibility with older OpenFlow clients.
> Usually if we need a new feature in an action we add a new version that
> has that feature, e.g. NXAST_SAMPLE2.  The IPFIX enhancements that Benli
> Ye are working on already do that for another reason, so you should
> probably coordinate so that we don't end up with three versions of the
> sample action.

Sure, I will do that.

>
> I'm not sure why CHECK_TRUNC_USERSPACE exists, because I think that your
> patch implements the new action in userspace.

When translating sflow header config, only if it runs kernel datapath,
I will program truncate to sample's actions list, ex:
"sample(trunc(64), userspace(...))". If it runs userspace datapath,
then it falls back to original way "sample(userspace(...))" and let
sflow code cut the packet to 'header' size.

The sflow testcase in system-traffic.at is created only for kernel dp,
so CHECK_TRUNC_USERSPACE skips it when running in userspace.

Regards,
William
Ben Pfaff June 10, 2016, 3:16 a.m. UTC | #3
On Thu, Jun 09, 2016 at 06:06:47PM -0700, William Tu wrote:
> > I'm not sure why CHECK_TRUNC_USERSPACE exists, because I think that your
> > patch implements the new action in userspace.
> 
> When translating sflow header config, only if it runs kernel datapath,
> I will program truncate to sample's actions list, ex:
> "sample(trunc(64), userspace(...))". If it runs userspace datapath,
> then it falls back to original way "sample(userspace(...))" and let
> sflow code cut the packet to 'header' size.

Why are the two datapaths treated differently?  Software is easier to
test if all the cases are similar, when possible.
William Tu June 10, 2016, 5:20 a.m. UTC | #4
Hi Ben,

Because for sFlow, it doesn't have any benefit to do
"sample(truncate(n), userspace(...))" in userspace datapath. I tried
to implement it by truncating the packet at OVS_ACTION_ATTR_USERSPACE
in dp_execute_cb() but it looks weird. And currently I couldn't think
of any other use case so I let sFlow translates differently for kernel
and userspace dp.

Regards,
William


On Thu, Jun 9, 2016 at 8:16 PM, Ben Pfaff <blp@ovn.org> wrote:
> On Thu, Jun 09, 2016 at 06:06:47PM -0700, William Tu wrote:
>> > I'm not sure why CHECK_TRUNC_USERSPACE exists, because I think that your
>> > patch implements the new action in userspace.
>>
>> When translating sflow header config, only if it runs kernel datapath,
>> I will program truncate to sample's actions list, ex:
>> "sample(trunc(64), userspace(...))". If it runs userspace datapath,
>> then it falls back to original way "sample(userspace(...))" and let
>> sflow code cut the packet to 'header' size.
>
> Why are the two datapaths treated differently?  Software is easier to
> test if all the cases are similar, when possible.
Ben Pfaff June 10, 2016, 3:01 p.m. UTC | #5
Is there a significant cost to truncating in the userspace datapath, or
does it just "look weird"?

On Thu, Jun 09, 2016 at 10:20:31PM -0700, William Tu wrote:
> Hi Ben,
> 
> Because for sFlow, it doesn't have any benefit to do
> "sample(truncate(n), userspace(...))" in userspace datapath. I tried
> to implement it by truncating the packet at OVS_ACTION_ATTR_USERSPACE
> in dp_execute_cb() but it looks weird. And currently I couldn't think
> of any other use case so I let sFlow translates differently for kernel
> and userspace dp.
> 
> Regards,
> William
> 
> 
> On Thu, Jun 9, 2016 at 8:16 PM, Ben Pfaff <blp@ovn.org> wrote:
> > On Thu, Jun 09, 2016 at 06:06:47PM -0700, William Tu wrote:
> >> > I'm not sure why CHECK_TRUNC_USERSPACE exists, because I think that your
> >> > patch implements the new action in userspace.
> >>
> >> When translating sflow header config, only if it runs kernel datapath,
> >> I will program truncate to sample's actions list, ex:
> >> "sample(trunc(64), userspace(...))". If it runs userspace datapath,
> >> then it falls back to original way "sample(userspace(...))" and let
> >> sflow code cut the packet to 'header' size.
> >
> > Why are the two datapaths treated differently?  Software is easier to
> > test if all the cases are similar, when possible.
William Tu June 10, 2016, 6:51 p.m. UTC | #6
Hi Ben,
No significant cost. Since the implementation is simple, let me add it
in next version and we can decide to have it or not.
William

On Fri, Jun 10, 2016 at 8:01 AM, Ben Pfaff <blp@ovn.org> wrote:
> Is there a significant cost to truncating in the userspace datapath, or
> does it just "look weird"?
>
> On Thu, Jun 09, 2016 at 10:20:31PM -0700, William Tu wrote:
>> Hi Ben,
>>
>> Because for sFlow, it doesn't have any benefit to do
>> "sample(truncate(n), userspace(...))" in userspace datapath. I tried
>> to implement it by truncating the packet at OVS_ACTION_ATTR_USERSPACE
>> in dp_execute_cb() but it looks weird. And currently I couldn't think
>> of any other use case so I let sFlow translates differently for kernel
>> and userspace dp.
>>
>> Regards,
>> William
>>
>>
>> On Thu, Jun 9, 2016 at 8:16 PM, Ben Pfaff <blp@ovn.org> wrote:
>> > On Thu, Jun 09, 2016 at 06:06:47PM -0700, William Tu wrote:
>> >> > I'm not sure why CHECK_TRUNC_USERSPACE exists, because I think that your
>> >> > patch implements the new action in userspace.
>> >>
>> >> When translating sflow header config, only if it runs kernel datapath,
>> >> I will program truncate to sample's actions list, ex:
>> >> "sample(trunc(64), userspace(...))". If it runs userspace datapath,
>> >> then it falls back to original way "sample(userspace(...))" and let
>> >> sflow code cut the packet to 'header' size.
>> >
>> > Why are the two datapaths treated differently?  Software is easier to
>> > test if all the cases are similar, when possible.
diff mbox

Patch

diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h
index 11e48ff..2634868 100644
--- a/include/openvswitch/ofp-actions.h
+++ b/include/openvswitch/ofp-actions.h
@@ -789,6 +789,7 @@  struct ofpact_note {
 struct ofpact_sample {
     struct ofpact ofpact;
     uint16_t probability;  // Always >0.
+    uint16_t snaplen;
     uint32_t collector_set_id;
     uint32_t obs_domain_id;
     uint32_t obs_point_id;
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 7fb6869..27af9b9 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -4736,8 +4736,10 @@  struct nx_action_sample {
     ovs_be32 collector_set_id;      /* ID of collector set in OVSDB. */
     ovs_be32 obs_domain_id;         /* ID of sampling observation domain. */
     ovs_be32 obs_point_id;          /* ID of sampling observation point. */
+    ovs_be16 snaplen;               /* Max sampled packet size in byte. */
+    uint8_t pad[6];
 };
-OFP_ASSERT(sizeof(struct nx_action_sample) == 24);
+OFP_ASSERT(sizeof(struct nx_action_sample) == 32);
 
 static enum ofperr
 decode_NXAST_RAW_SAMPLE(const struct nx_action_sample *nas,
@@ -4751,6 +4753,7 @@  decode_NXAST_RAW_SAMPLE(const struct nx_action_sample *nas,
     sample->collector_set_id = ntohl(nas->collector_set_id);
     sample->obs_domain_id = ntohl(nas->obs_domain_id);
     sample->obs_point_id = ntohl(nas->obs_point_id);
+    sample->snaplen = ntohs(nas->snaplen);
 
     if (sample->probability == 0) {
         return OFPERR_OFPBAC_BAD_ARGUMENT;
@@ -4770,6 +4773,7 @@  encode_SAMPLE(const struct ofpact_sample *sample,
     nas->collector_set_id = htonl(sample->collector_set_id);
     nas->obs_domain_id = htonl(sample->obs_domain_id);
     nas->obs_point_id = htonl(sample->obs_point_id);
+    nas->snaplen = htons(sample->snaplen);
 }
 
 /* Parses 'arg' as the argument to a "sample" action, and appends such an
@@ -4798,6 +4802,8 @@  parse_SAMPLE(char *arg, struct ofpbuf *ofpacts,
             error = str_to_u32(value, &os->obs_domain_id);
         } else if (!strcmp(key, "obs_point_id")) {
             error = str_to_u32(value, &os->obs_point_id);
+        } else if (!strcmp(key, "snaplen")) {
+            error = str_to_u16(value, "snaplen", &os->snaplen);
         } else {
             error = xasprintf("invalid key \"%s\" in \"sample\" argument",
                               key);
@@ -4816,11 +4822,13 @@  static void
 format_SAMPLE(const struct ofpact_sample *a, struct ds *s)
 {
     ds_put_format(s, "%ssample(%s%sprobability=%s%"PRIu16
+                  ",%ssnaplen=%s%"PRIu16
                   ",%scollector_set_id=%s%"PRIu32
                   ",%sobs_domain_id=%s%"PRIu32
                   ",%sobs_point_id=%s%"PRIu32"%s)%s",
                   colors.paren, colors.end,
                   colors.param, colors.end, a->probability,
+                  colors.param, colors.end, a->snaplen,
                   colors.param, colors.end, a->collector_set_id,
                   colors.param, colors.end, a->obs_domain_id,
                   colors.param, colors.end, a->obs_point_id,
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index 5d26b7c..ae0b1ec 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -556,6 +556,16 @@  dpif_sflow_get_probability(const struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
     return probability;
 }
 
+uint16_t
+dpif_sflow_get_header_len(const struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
+{
+    uint16_t header_len;
+    ovs_mutex_lock(&mutex);
+    header_len = ds->options->header_len;
+    ovs_mutex_unlock(&mutex);
+    return header_len;
+}
+
 void
 dpif_sflow_unref(struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
 {
@@ -1223,6 +1233,7 @@  dpif_sflow_cookie_num_outputs(const union user_action_cookie *cookie)
 void
 dpif_sflow_received(struct dpif_sflow *ds, const struct dp_packet *packet,
 		    const struct flow *flow, odp_port_t odp_in_port,
+		    const uint16_t cutlen,
 		    const union user_action_cookie *cookie,
 		    const struct dpif_sflow_actions *sflow_actions)
     OVS_EXCLUDED(mutex)
@@ -1268,11 +1279,13 @@  dpif_sflow_received(struct dpif_sflow *ds, const struct dp_packet *packet,
     header = &hdrElem.flowType.header;
     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 = dp_packet_size(packet) + 4;
+     * but it has already been stripped,  so we need to add 4 here.
+     * The frame might be truncated by cutlen byte before upcall,
+     * so we need to add it back. */
+    header->frame_length = dp_packet_size(packet) + cutlen + 4;
     /* Ethernet FCS stripped off. */
     header->stripped = 4;
-    header->header_length = MIN(dp_packet_size(packet),
+    header->header_length = MIN(dp_packet_size(packet) + cutlen,
                                 sampler->sFlowFsMaximumHeaderSize);
     header->header_bytes = dp_packet_data(packet);
 
diff --git a/ofproto/ofproto-dpif-sflow.h b/ofproto/ofproto-dpif-sflow.h
index 014e6cc..e7d6c3f 100644
--- a/ofproto/ofproto-dpif-sflow.h
+++ b/ofproto/ofproto-dpif-sflow.h
@@ -56,6 +56,7 @@  struct dpif_sflow *dpif_sflow_ref(const struct dpif_sflow *);
 void dpif_sflow_unref(struct dpif_sflow *);
 
 uint32_t dpif_sflow_get_probability(const struct dpif_sflow *);
+uint16_t dpif_sflow_get_header_len(const struct dpif_sflow *);
 
 void dpif_sflow_set_options(struct dpif_sflow *,
                             const struct ofproto_sflow_options *);
@@ -75,6 +76,7 @@  void dpif_sflow_read_actions(const struct flow *,
 
 void dpif_sflow_received(struct dpif_sflow *, const struct dp_packet *,
                          const struct flow *, odp_port_t odp_port,
+                         const uint16_t cutlen,
                          const union user_action_cookie *,
 			 const struct dpif_sflow_actions *);
 
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index 61e7494..96e8d79 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -1238,7 +1238,9 @@  process_upcall(struct udpif *udpif, struct upcall *upcall,
                 }
             }
             dpif_sflow_received(upcall->sflow, packet, flow,
-                                flow->in_port.odp_port, &cookie,
+                                flow->in_port.odp_port,
+                                upcall->cutlen,
+                                &cookie,
                                 actions_len > 0 ? &sflow_actions : NULL);
         }
         break;
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index a2cf7ed..bd99dd3 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2598,7 +2598,8 @@  xlate_normal(struct xlate_ctx *ctx)
 
 /* Appends a "sample" action for sFlow or IPFIX to 'ctx->odp_actions'.  The
  * 'probability' is the number of packets out of UINT32_MAX to sample.  The
- * 'cookie' (of length 'cookie_size' bytes) is passed back in the callback for
+ * 'snaplen' is the maximum sampled packet size in bytes.  The 'cookie'
+ * (of length 'cookie_size' bytes) is passed back in the callback for
  * each sampled packet.  'tunnel_out_port', if not ODPP_NONE, is added as the
  * OVS_USERSPACE_ATTR_EGRESS_TUN_PORT attribute.  If 'include_actions', an
  * OVS_USERSPACE_ATTR_ACTIONS attribute is added.
@@ -2606,6 +2607,7 @@  xlate_normal(struct xlate_ctx *ctx)
 static size_t
 compose_sample_action(struct xlate_ctx *ctx,
                       const uint32_t probability,
+                      const uint16_t snaplen,
                       const union user_action_cookie *cookie,
                       const size_t cookie_size,
                       const odp_port_t tunnel_out_port,
@@ -2619,6 +2621,21 @@  compose_sample_action(struct xlate_ctx *ctx,
     size_t actions_offset = nl_msg_start_nested(ctx->odp_actions,
                                                 OVS_SAMPLE_ATTR_ACTIONS);
 
+    /* Enable only when it is kernel datapath with truncate action support.
+     * Userspace datapath does not need this optimization. */
+    bool support_trunc = ctx->xbridge->support.trunc;
+    if (!strcmp(dpif_type(ctx->xbridge->dpif), "system") &&
+            support_trunc) {
+        if (snaplen > 0 && snaplen < UINT16_MAX) {
+            struct ovs_action_trunc *trunc;
+
+            trunc = nl_msg_put_unspec_uninit(ctx->odp_actions,
+                            OVS_ACTION_ATTR_TRUNC,
+                            sizeof *trunc);
+            trunc->max_len = snaplen;
+        }
+    }
+
     odp_port_t odp_port = ofp_port_to_odp_port(
         ctx->xbridge, ctx->xin->flow.in_port.ofp_port);
     uint32_t pid = dpif_port_get_pid(ctx->xbridge->dpif, odp_port,
@@ -2651,6 +2668,7 @@  compose_sflow_action(struct xlate_ctx *ctx)
 
     union user_action_cookie cookie = { .type = USER_ACTION_COOKIE_SFLOW };
     return compose_sample_action(ctx, dpif_sflow_get_probability(sflow),
+                                 dpif_sflow_get_header_len(sflow),
                                  &cookie, sizeof cookie.sflow, ODPP_NONE,
                                  true);
 }
@@ -2696,6 +2714,7 @@  compose_ipfix_action(struct xlate_ctx *ctx, odp_port_t output_odp_port)
     };
     compose_sample_action(ctx,
                           dpif_ipfix_get_bridge_exporter_probability(ipfix),
+                          0, /* snaplen = 0 means 65535. */
                           &cookie, sizeof cookie.ipfix, tunnel_out_port,
                           false);
 }
@@ -4197,6 +4216,7 @@  xlate_sample_action(struct xlate_ctx *ctx,
     /* Scale the probability from 16-bit to 32-bit while representing
      * the same percentage. */
     uint32_t probability = (os->probability << 16) | os->probability;
+    uint16_t snaplen = os->snaplen;
 
     if (!ctx->xbridge->support.variable_length_userdata) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
@@ -4218,7 +4238,8 @@  xlate_sample_action(struct xlate_ctx *ctx,
             .obs_point_id = os->obs_point_id,
         }
     };
-    compose_sample_action(ctx, probability, &cookie, sizeof cookie.flow_sample,
+    compose_sample_action(ctx, probability, snaplen, &cookie,
+                          sizeof cookie.flow_sample,
                           ODPP_NONE, false);
 }
 
diff --git a/tests/odp.at b/tests/odp.at
index 9be730a..926a908 100644
--- a/tests/odp.at
+++ b/tests/odp.at
@@ -330,6 +330,8 @@  ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random))
 ct(commit,nat(src=[[fe80::20c:29ff:fe88:1]]-[[fe80::20c:29ff:fe88:a18b]]:255-4096,random))
 ct(commit,helper=ftp,nat(src=10.1.1.240-10.1.1.255))
 trunc(100)
+sample(sample=9.7%,actions(trunc(100)))
+sample(sample=9.7%,actions(trunc(100),userspace(pid=6633,sFlow(vid=9,pcp=7,output=10),actions)))
 ])
 AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0],
   [`cat actions.txt`
diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at
index bd9cf41..bc44c32 100644
--- a/tests/ofp-actions.at
+++ b/tests/ofp-actions.at
@@ -124,8 +124,8 @@  ffff 0040 00002320 0025   000000000000 dnl
 # actions=dec_ttl(32768,12345,90,765,1024)
 ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000
 
-# actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-ffff 0018 00002320 001d 3039 00005BA0 00008707 0000B26E
+# actions=sample(probability=12345,snaplen=100,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+ffff 0020 00002320 001d 3039 00005BA0 00008707 0000B26E 0064 000000000000
 
 # bad OpenFlow10 actions: OFPBAC_BAD_LEN
 & ofp_actions|WARN|OpenFlow action OFPAT_OUTPUT length 240 exceeds action buffer length 8
@@ -410,8 +410,8 @@  ffff 0010 00002320 0014 04d2 162e 02 00
 # actions=dec_ttl(32768,12345,90,765,1024)
 ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000
 
-# actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
-ffff 0018 00002320 001d 3039 00005BA0 00008707 0000B26E
+# actions=sample(probability=12345,snaplen=100,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+ffff 0020 00002320 001d 3039 00005BA0 00008707 0000B26E 0064 000000000000
 
 # bad OpenFlow11 actions: OFPBAC_BAD_OUT_PORT
 & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_OUT_PORT):
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 399c895..5c1605f 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -5446,6 +5446,23 @@  AT_CHECK([tail -1 stdout], [0],
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto-dpif - truncate and userspace])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2
+AT_CHECK([ovs-vsctl -- --id=@s create sFlow agent=br0 \
+            target=\"127.0.0.1:6344\" header=64 sampling=1 polling=1 \
+            -- set bridge br0 sflow=@s], [0], [stdout])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+
+dnl truncate and userspace only work when it is under kernel datapath.
+dnl Thus, the datapath actions here shows no truncate action.
+AT_CHECK([tail -1 stdout], [0],
+   [Datapath actions: sample(sample=100.0%,actions(userspace(pid=0,sFlow(vid=0,pcp=0,output=1073742080),actions)))
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - sFlow packet sampling - IPv4 collector])
 CHECK_SFLOW_SAMPLING_PACKET([127.0.0.1])
 AT_CLEANUP
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index fa7ed48..70bbfb9 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -159,7 +159,7 @@  ip,actions=set_field:10.4.3.77->ip_src
 sctp actions=drop
 sctp actions=drop
 in_port=0 actions=resubmit:0
-actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+actions=sample(probability=12345,snaplen=0,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 actions=ct(nat)
 actions=ct(commit,nat(dst))
 actions=ct(commit,nat(src))
@@ -189,7 +189,7 @@  OFPT_FLOW_MOD: ADD ip actions=mod_nw_src:10.4.3.77
 OFPT_FLOW_MOD: ADD sctp actions=drop
 OFPT_FLOW_MOD: ADD sctp actions=drop
 OFPT_FLOW_MOD: ADD in_port=0 actions=resubmit:0
-OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+OFPT_FLOW_MOD: ADD actions=sample(probability=12345,snaplen=0,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 OFPT_FLOW_MOD: ADD actions=ct(nat)
 OFPT_FLOW_MOD: ADD actions=ct(commit,nat(dst))
 OFPT_FLOW_MOD: ADD actions=ct(commit,nat(src))
@@ -218,7 +218,7 @@  ip,actions=mod_nw_ttl:1,set_field:10.4.3.77->ip_src
 sctp actions=drop
 sctp actions=drop
 in_port=0 actions=resubmit:0
-actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+actions=sample(probability=12345,snaplen=0,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ]])
 
 AT_CHECK([ovs-ofctl --protocols OpenFlow11 parse-flows flows.txt
@@ -237,7 +237,7 @@  OFPT_FLOW_MOD (OF1.1): ADD ip actions=mod_nw_ttl:1,mod_nw_src:10.4.3.77
 OFPT_FLOW_MOD (OF1.1): ADD sctp actions=drop
 OFPT_FLOW_MOD (OF1.1): ADD sctp actions=drop
 OFPT_FLOW_MOD (OF1.1): ADD in_port=0 actions=resubmit:0
-OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,snaplen=0,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ]])
 AT_CLEANUP
 
@@ -259,7 +259,7 @@  udp actions=mod_tp_src:1111
 ip actions=mod_nw_src:10.1.1.2,mod_nw_dst:192.168.10.1,mod_nw_ttl:1,mod_nw_tos:16,mod_nw_ecn:2
 in_port=0 actions=mod_dl_src:11:22:33:44:55:66,mod_dl_dst:10:20:30:40:50:60
 in_port=0 actions=resubmit:0
-actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+actions=sample(probability=12345,snaplen=0,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ]])
 
 AT_CHECK([ovs-ofctl --protocols OpenFlow12 parse-flows flows.txt
@@ -282,7 +282,7 @@  OFPT_FLOW_MOD (OF1.2): ADD udp actions=set_field:1111->udp_src
 OFPT_FLOW_MOD (OF1.2): ADD ip actions=set_field:10.1.1.2->ip_src,set_field:192.168.10.1->ip_dst,mod_nw_ttl:1,set_field:4->ip_dscp,set_field:2->nw_ecn
 OFPT_FLOW_MOD (OF1.2): ADD in_port=0 actions=set_field:11:22:33:44:55:66->eth_src,set_field:10:20:30:40:50:60->eth_dst
 OFPT_FLOW_MOD (OF1.2): ADD in_port=0 actions=resubmit:0
-OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,snaplen=0,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ]])
 AT_CLEANUP
 
@@ -374,7 +374,7 @@  send_flow_rem,actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[
 check_overlap,actions=output:1,exit,output:2
 tcp,actions=fin_timeout(idle_timeout=5,hard_timeout=15)
 actions=controller(max_len=123,reason=invalid_ttl,id=555)
-actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+actions=sample(probability=12345,snaplen=0,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ip,actions=ct(commit,zone=5)
 ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[])))
 ip,actions=ct(commit,exec(load(0x1->NXM_NX_CT_LABEL[])))
@@ -418,7 +418,7 @@  NXT_FLOW_MOD: ADD table:255 send_flow_rem actions=output:1,output:NXM_NX_REG0[],
 NXT_FLOW_MOD: ADD table:255 check_overlap actions=output:1,exit,output:2
 NXT_FLOW_MOD: ADD table:255 tcp actions=fin_timeout(idle_timeout=5,hard_timeout=15)
 NXT_FLOW_MOD: ADD table:255 actions=controller(reason=invalid_ttl,max_len=123,id=555)
-NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,snaplen=0,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,zone=5)
 NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[]))
 NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127]))
@@ -460,7 +460,7 @@  dl_dst=01:00:00:00:00:00/01:00:00:00:00:00,actions=drop
 dl_dst=00:00:00:00:00:00/01:00:00:00:00:00,actions=drop
 dl_dst=aa:bb:cc:dd:ee:ff/fe:ff:ff:ff:ff:ff,actions=drop
 dl_dst=aa:bb:cc:dd:ee:ff/00:00:00:00:00:00,actions=drop
-actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+actions=sample(probability=12345,snaplen=0,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ip,actions=ct(commit,zone=5)
 ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[[]])))
 ip,actions=ct(commit,exec(load(0x1->NXM_NX_CT_LABEL[[]])))
@@ -496,7 +496,7 @@  NXT_FLOW_MOD: ADD dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=drop
 NXT_FLOW_MOD: ADD dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=drop
 NXT_FLOW_MOD: ADD dl_dst=aa:bb:cc:dd:ee:ff/fe:ff:ff:ff:ff:ff actions=drop
 NXT_FLOW_MOD: ADD actions=drop
-NXT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+NXT_FLOW_MOD: ADD actions=sample(probability=12345,snaplen=0,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 NXT_FLOW_MOD: ADD ip actions=ct(commit,zone=5)
 NXT_FLOW_MOD: ADD ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[[]]))
 NXT_FLOW_MOD: ADD ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[[0..63]],load:0->NXM_NX_CT_LABEL[[64..127]]))
@@ -532,7 +532,7 @@  reg0=123,actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:55->NXM_NX_REG
 actions=move:OXM_OF_ETH_DST[]->OXM_OF_ETH_SRC[]
 actions=push:NXM_NX_REG0[0..31],pop:NXM_NX_REG0[]
 vlan_tci=0x1123/0x1fff,actions=drop
-actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+actions=sample(probability=12345,snaplen=0,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ip,actions=ct(commit,zone=5)
 ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[])))
 ip,actions=ct(commit,exec(load(1->NXM_NX_CT_LABEL[])))
@@ -568,7 +568,7 @@  NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) actions=move:NXM_NX_REG0[0..5]->NXM_NX_R
 NXT_FLOW_MOD: ADD <any> actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[]
 NXT_FLOW_MOD: ADD <any> actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[]
 NXT_FLOW_MOD: ADD NXM_OF_VLAN_TCI_W(1123/1fff) actions=drop
-NXT_FLOW_MOD: ADD <any> actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+NXT_FLOW_MOD: ADD <any> actions=sample(probability=12345,snaplen=0,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,zone=5)
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[]))
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127]))
diff --git a/tests/system-traffic.at b/tests/system-traffic.at
index 7c82514..0300cbd 100644
--- a/tests/system-traffic.at
+++ b/tests/system-traffic.at
@@ -418,6 +418,51 @@  n_bytes=100
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([datapath - truncate and userspace action])
+dnl Demonstrate that when truncate happens at kernel datapath, the
+dnl upcall netlink packet size is no longer the original size.
+CHECK_TRUNC_USERSPACE()
+OVS_TRAFFIC_VSWITCHD_START()
+
+dnl skip if it is check-userspace
+AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"])
+ADD_NAMESPACES(at_ns0, at_ns1)
+
+ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
+ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
+
+NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+
+dnl setup sflow
+AT_CHECK([ovs-vsctl -- \
+    --id=@s create sFlow agent=br0 target=\"127.0.0.1:6344\" \
+    header=64 sampling=1 polling=1 -- set bridge br0 sflow=@s
+], [0], [stdout])
+
+dnl ofproto/trace
+AT_CHECK([ovs-appctl ofproto/trace ovs-system 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout | sed 's/pid=[[0-9]]*/pid=/g'], [0],
+    [Datapath actions: sample(sample=100.0%,actions(trunc(64),userspace(pid=,sFlow(vid=0,pcp=0,output=2147483650),actions))),1,3
+])
+AT_CHECK([ovs-appctl vlog/set netlink_socket::DBG])
+
+dnl send ping with 1024 bytes
+NS_CHECK_EXEC([at_ns0], [ping -s 1024 -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+
+dnl filter out nl_sock_recv__ message with userspace action (cmd=2)
+AT_CHECK([grep "nl_sock_recv__.*ovs_packet.*cmd=2" ovs-vswitchd.log > nl_sock.log])
+AT_CHECK([sed -n 's/.*len:\([[0-9]]*\).*/\1/p' nl_sock.log | sort -r | tail -1 > max_pkt_size])
+max_pkt_size=$(cat max_pkt_size)
+dnl with truncate, ping -s 1024 should generate the nl packet size less than 1000 bytes
+AT_CHECK([if [[ "$max_pkt_size" -gt "1000" ]]; then exit 1; fi], [0])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([conntrack - controller])
 CHECK_CONNTRACK()
 OVS_TRAFFIC_VSWITCHD_START()
diff --git a/tests/system-userspace-macros.at b/tests/system-userspace-macros.at
index c09a4aa..e7c7628 100644
--- a/tests/system-userspace-macros.at
+++ b/tests/system-userspace-macros.at
@@ -66,3 +66,12 @@  m4_define([CONFIGURE_VETH_OFFLOADS],
 m4_define([CHECK_CONNTRACK],
     [AT_SKIP_IF(true)]
 )
+
+# CHECK_TRUNC_USERSPACE()
+#
+# Skip truncate and userspace action, used by sFlow, when testsuite
+# is running in userspace.
+#
+m4_define([CHECK_TRUNC_USERSPACE],
+    [AT_SKIP_IF(true)]
+)