diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 071ec14..f417b25 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -453,6 +453,9 @@ struct dp_netdev_flow {
     struct ovs_refcount ref_cnt;
 
     bool dead;
+    bool has_mark;               /* A flag to tell whether this flow has a
+                                    valid mark asscoiated with it. */
+    uint32_t mark;               /* Unique flow mark assiged to a flow */
 
     /* Statistics. */
     struct dp_netdev_flow_stats stats;
@@ -1833,6 +1836,82 @@ dp_netdev_pmd_find_dpcls(struct dp_netdev_pmd_thread *pmd,
     return cls;
 }
 
+/*
+ * An array to map from the flow mark (the index) to dp netdev flow.
+ * Initial size is set to 1024, it will be resized when it's not
+ * big enough.
+ */
+struct flow_mark_map {
+    size_t max;
+    struct ovs_mutex mutex;
+    struct id_pool *mark_pool;
+    struct dp_netdev_flow **flows;
+};
+
+static struct flow_mark_map flow_mark_map = {
+    .max = 1024,
+    .mutex = OVS_MUTEX_INITIALIZER,
+};
+
+static bool
+dp_netdev_alloc_flow_mark(uint32_t *mark)
+{
+    bool succeed = true;
+
+    ovs_mutex_lock(&flow_mark_map.mutex);
+
+    /* Haven't initiated yet, do it here */
+    if (!flow_mark_map.flows) {
+        flow_mark_map.flows = xzalloc(sizeof(struct dp_netdev_flow *) *
+                                      flow_mark_map.max);
+        flow_mark_map.mark_pool = id_pool_create(0, UINT32_MAX / 2);
+    }
+
+    do {
+        if (!id_pool_alloc_id(flow_mark_map.mark_pool, mark)) {
+            succeed = false;
+            break;
+        }
+
+        if (*mark >= flow_mark_map.max) {
+            flow_mark_map.flows = xrealloc(flow_mark_map.flows,
+                                           flow_mark_map.max * 2 *
+                                           sizeof(struct dp_netdev_flow *));
+            memset(&flow_mark_map.flows[flow_mark_map.max], 0,
+                   flow_mark_map.max * sizeof(struct dp_netdev_flow *));
+            flow_mark_map.max *= 2;
+
+            break;
+        }
+    } while (flow_mark_map.flows[*mark]);
+
+    ovs_mutex_unlock(&flow_mark_map.mutex);
+
+    return succeed;
+}
+
+static void
+dp_netdev_install_flow_mark_map(const uint32_t mark,
+                                struct dp_netdev_flow *netdev_flow)
+{
+    ovs_mutex_lock(&flow_mark_map.mutex);
+    if (mark < flow_mark_map.max) {
+        flow_mark_map.flows[mark] = netdev_flow;
+    }
+    ovs_mutex_unlock(&flow_mark_map.mutex);
+}
+
+static void
+dp_netdev_remove_flow_mark_map(const uint32_t mark)
+{
+    ovs_mutex_lock(&flow_mark_map.mutex);
+    id_pool_free_id(flow_mark_map.mark_pool, mark);
+    if (mark < flow_mark_map.max) {
+        flow_mark_map.flows[mark] = NULL;
+    }
+    ovs_mutex_unlock(&flow_mark_map.mutex);
+}
+
 static void
 dp_netdev_pmd_remove_flow(struct dp_netdev_pmd_thread *pmd,
                           struct dp_netdev_flow *flow)
@@ -1846,6 +1925,10 @@ dp_netdev_pmd_remove_flow(struct dp_netdev_pmd_thread *pmd,
     ovs_assert(cls != NULL);
     dpcls_remove(cls, &flow->cr);
     cmap_remove(&pmd->flow_table, node, dp_netdev_flow_hash(&flow->ufid));
+    if (flow->has_mark) {
+        dp_netdev_remove_flow_mark_map(flow->mark);
+        flow->has_mark = false;
+    }
     flow->dead = true;
 
     dp_netdev_flow_unref(flow);
@@ -2425,6 +2508,55 @@ out:
     return error;
 }
 
+static void
+try_netdev_flow_put(struct dp_netdev_pmd_thread *pmd, odp_port_t in_port,
+                    struct dp_netdev_flow *flow, struct match *match,
+                    const ovs_u128 *ufid, const struct nlattr *actions,
+                    size_t actions_len)
+{
+    struct offload_info info;
+    struct dp_netdev_port *port;
+    bool modification = flow->has_mark;
+    const char *op = modification ? "modify" : "install";
+    int ret;
+
+    port = dp_netdev_lookup_port(pmd->dp, in_port);
+    if (!port) {
+        return;
+    }
+
+    if (modification) {
+        info.flow_mark = flow->mark;
+    } else {
+        if (!netdev_is_flow_api_enabled()) {
+            return;
+        }
+
+        if (!dp_netdev_alloc_flow_mark(&info.flow_mark)) {
+            VLOG_ERR("failed to allocate flow mark!\n");
+            return;
+        }
+    }
+
+    ret = netdev_flow_put(port->netdev, match,
+                          CONST_CAST(struct nlattr *, actions),
+                          actions_len, ufid, &info, NULL);
+    if (ret) {
+        VLOG_ERR("failed to %s netdev flow with mark %u\n",
+                 op, info.flow_mark);
+        return;
+    }
+
+    if (!modification) {
+        flow->has_mark = true;
+        flow->mark = info.flow_mark;
+        dp_netdev_install_flow_mark_map(info.flow_mark, flow);
+    }
+
+    VLOG_INFO("succeed to %s netdev flow with mark %u\n",
+              op, flow->mark);
+}
+
 static struct dp_netdev_flow *
 dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
                    struct match *match, const ovs_u128 *ufid,
@@ -2460,6 +2592,7 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
     memset(&flow->stats, 0, sizeof flow->stats);
     flow->dead = false;
     flow->batch = NULL;
+    flow->has_mark = false;
     *CONST_CAST(unsigned *, &flow->pmd_id) = pmd->core_id;
     *CONST_CAST(struct flow *, &flow->flow) = match->flow;
     *CONST_CAST(ovs_u128 *, &flow->ufid) = *ufid;
@@ -2475,6 +2608,9 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
     cmap_insert(&pmd->flow_table, CONST_CAST(struct cmap_node *, &flow->node),
                 dp_netdev_flow_hash(&flow->ufid));
 
+    try_netdev_flow_put(pmd, in_port, flow, match, ufid,
+                        actions, actions_len);
+
     if (OVS_UNLIKELY(!VLOG_DROP_DBG((&upcall_rl)))) {
         struct ds ds = DS_EMPTY_INITIALIZER;
         struct ofpbuf key_buf, mask_buf;
@@ -2555,6 +2691,7 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd,
         if (put->flags & DPIF_FP_MODIFY) {
             struct dp_netdev_actions *new_actions;
             struct dp_netdev_actions *old_actions;
+            odp_port_t in_port = netdev_flow->flow.in_port.odp_port;
 
             new_actions = dp_netdev_actions_create(put->actions,
                                                    put->actions_len);
@@ -2562,6 +2699,9 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd,
             old_actions = dp_netdev_flow_get_actions(netdev_flow);
             ovsrcu_set(&netdev_flow->actions, new_actions);
 
+            try_netdev_flow_put(pmd, in_port, netdev_flow, match, ufid,
+                                put->actions, put->actions_len);
+
             if (stats) {
                 get_dpif_flow_stats(netdev_flow, stats);
             }
diff --git a/lib/netdev.h b/lib/netdev.h
index f8482f7..2003165 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -188,6 +188,12 @@ void netdev_send_wait(struct netdev *, int qid);
 struct offload_info {
     const struct dpif_class *dpif_class;
     ovs_be16 tp_dst_port; /* Destination port for tunnel in SET action */
+
+    /*
+     * The flow mark id assigened to the flow. If any pkts hit the flow,
+     * it will be in the pkt meta data.
+     */
+    uint32_t flow_mark;
 };
 struct dpif_class;
 struct netdev_flow_dump;
