diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 02df8f11e..6dd5ff055 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -8459,6 +8459,9 @@ const struct dpif_class dpif_netdev_class = {
     dpif_netdev_bond_add,
     dpif_netdev_bond_del,
     dpif_netdev_bond_stats_get,
+    NULL,                       /* psample_enabled */
+    NULL,                       /* psample_poll */
+    NULL,                       /* psample_poll_wait */
 };
 
 static void
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 79afef5f1..55199d35e 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -4093,6 +4093,9 @@ const struct dpif_class dpif_netlink_class = {
     NULL,                       /* bond_add */
     NULL,                       /* bond_del */
     NULL,                       /* bond_stats_get */
+    NULL,                       /* psample_enabled */
+    NULL,                       /* psample_poll */
+    NULL,                       /* psample_poll_wait */
 };
 
 static int
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
index 0e024c1c9..bfebf4b01 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -628,6 +628,17 @@ struct dpif_class {
      * sufficient to store BOND_BUCKETS number of elements. */
     int (*bond_stats_get)(struct dpif *dpif, uint32_t bond_id,
                           uint64_t *n_bytes);
+
+    /* Check if psample thread should be created or not */
+    bool (*psample_enabled)(const struct dpif *dpif);
+
+    /* psample thread was created. Poll psample netlink to receive sampled
+     * packets */
+    int (*psample_poll)(const struct dpif *dpif,
+                        struct dpif_upcall_psample *dupcall);
+
+    /* Arranges for the poll loop to wake up when 'psample_poll' returns */
+    void (*psample_poll_wait)(const struct dpif *dpif);
 };
 
 extern const struct dpif_class dpif_netlink_class;
diff --git a/lib/dpif.c b/lib/dpif.c
index 7cac3a629..b39ec8560 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -2018,3 +2018,27 @@ dpif_bond_stats_get(struct dpif *dpif, uint32_t bond_id,
            ? dpif->dpif_class->bond_stats_get(dpif, bond_id, n_bytes)
            : EOPNOTSUPP;
 }
+
+int
+dpif_psample_poll(struct dpif *dpif, struct dpif_upcall_psample *dupcall)
+{
+    return dpif->dpif_class->psample_poll
+           ? dpif->dpif_class->psample_poll(dpif, dupcall)
+           : EOPNOTSUPP;
+}
+
+void
+dpif_psample_poll_wait(struct dpif *dpif)
+{
+    if (dpif->dpif_class->psample_poll_wait) {
+        dpif->dpif_class->psample_poll_wait(dpif);
+    }
+}
+
+bool
+dpif_psample_enabled(struct dpif *dpif)
+{
+    return dpif->dpif_class->psample_enabled
+           ? dpif->dpif_class->psample_enabled(dpif)
+           : false;
+}
diff --git a/lib/dpif.h b/lib/dpif.h
index 2d52f0186..49f7a4238 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -825,6 +825,33 @@ struct dpif_upcall {
     struct nlattr *actions;    /* Argument to OVS_ACTION_ATTR_USERSPACE. */
 };
 
+/* When offloading sample action to TC, userspace creates a unique ID
+ * to map sFlow action and tunnel info and passes this ID to kernel instead
+ * of the sFlow info. psample will send this ID and sampled packet to
+ * userspace. Using the ID, userspace can recover the sFlow info and send
+ * sampled packet to the right sFlow monitoring host.
+ */
+struct dpif_sflow_attr {
+    const struct nlattr *sflow; /* sFlow action */
+    size_t sflow_len;           /* Length of 'sflow' in bytes. */
+
+    void *userdata;             /* struct user_action_cookie */
+    size_t userdata_len;        /* struct user_action_cookie length */
+
+    struct flow_tnl *tunnel;    /* Tunnel info */
+};
+
+/* A sampled packet passed up from driver to userspace.
+ *
+ * After offloading sample action to TC, driver will send sampled packets
+ * to userspace using psample.
+ */
+struct dpif_upcall_psample {
+    struct dp_packet packet;    /* packet data */
+    uint32_t iifindex;          /* input ifindex */
+    const struct dpif_sflow_attr *sflow_attr;
+};
+
 /* A callback to notify higher layer of dpif about to be purged, so that
  * higher layer could try reacting to this (e.g. grabbing all flow stats
  * before they are gone).  This function is currently implemented only by
@@ -903,6 +930,11 @@ int dpif_bond_del(struct dpif *, uint32_t bond_id);
 int dpif_bond_stats_get(struct dpif *, uint32_t bond_id, uint64_t *n_bytes);
 bool dpif_supports_lb_output_action(const struct dpif *);
 
+/* psample */
+int dpif_psample_poll(struct dpif *, struct dpif_upcall_psample *);
+void dpif_psample_poll_wait(struct dpif *);
+bool dpif_psample_enabled(struct dpif *);
+
 
 /* Miscellaneous. */
 
