diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index 054a9b6..d89a0fb 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -2275,7 +2275,7 @@ netdev_linux_set_policing(struct netdev *netdev_,
 
     COVERAGE_INC(netdev_set_policing);
     /* Remove any existing ingress qdisc. */
-    error = tc_add_del_ingress_qdisc(ifindex, false);
+    error = tc_add_del_ingress_qdisc(ifindex, false, 0);
     if (error) {
         VLOG_WARN_RL(&rl, "%s: removing policing failed: %s",
                      netdev_name, ovs_strerror(error));
@@ -2283,7 +2283,7 @@ netdev_linux_set_policing(struct netdev *netdev_,
     }
 
     if (kbits_rate) {
-        error = tc_add_del_ingress_qdisc(ifindex, true);
+        error = tc_add_del_ingress_qdisc(ifindex, true, 0);
         if (error) {
             VLOG_WARN_RL(&rl, "%s: adding policing qdisc failed: %s",
                          netdev_name, ovs_strerror(error));
diff --git a/lib/netdev-tc-offloads.c b/lib/netdev-tc-offloads.c
index 0d8cc2c..14558ad 100644
--- a/lib/netdev-tc-offloads.c
+++ b/lib/netdev-tc-offloads.c
@@ -44,6 +44,7 @@ static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
 
 static struct hmap ufid_tc = HMAP_INITIALIZER(&ufid_tc);
 static bool multi_mask_per_prio = false;
+static bool block_support = false;
 
 struct netlink_field {
     int offset;
@@ -307,6 +308,7 @@ int
 netdev_tc_flow_flush(struct netdev *netdev)
 {
     int ifindex = netdev_get_ifindex(netdev);
+    uint32_t block_id = 0;
 
     if (ifindex < 0) {
         VLOG_ERR_RL(&error_rl, "flow_flush: failed to get ifindex for %s: %s",
@@ -314,7 +316,7 @@ netdev_tc_flow_flush(struct netdev *netdev)
         return -ifindex;
     }
 
-    return tc_flush(ifindex);
+    return tc_flush(ifindex, block_id);
 }
 
 int
@@ -322,6 +324,7 @@ netdev_tc_flow_dump_create(struct netdev *netdev,
                            struct netdev_flow_dump **dump_out)
 {
     struct netdev_flow_dump *dump;
+    uint32_t block_id = 0;
     int ifindex;
 
     ifindex = netdev_get_ifindex(netdev);
@@ -334,7 +337,7 @@ netdev_tc_flow_dump_create(struct netdev *netdev,
     dump = xzalloc(sizeof *dump);
     dump->nl_dump = xzalloc(sizeof *dump->nl_dump);
     dump->netdev = netdev_ref(netdev);
-    tc_dump_flower_start(ifindex, dump->nl_dump);
+    tc_dump_flower_start(ifindex, dump->nl_dump, block_id);
 
     *dump_out = dump;
 
@@ -890,6 +893,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
     struct flow *mask = &match->wc.masks;
     const struct flow_tnl *tnl = &match->flow.tunnel;
     struct tc_action *action;
+    uint32_t block_id = 0;
     struct nlattr *nla;
     size_t left;
     int prio = 0;
@@ -1097,7 +1101,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
     handle = get_ufid_tc_mapping(ufid, &prio, NULL);
     if (handle && prio) {
         VLOG_DBG_RL(&rl, "updating old handle: %d prio: %d", handle, prio);
-        tc_del_filter(ifindex, prio, handle);
+        tc_del_filter(ifindex, prio, handle, block_id);
     }
 
     if (!prio) {
@@ -1111,7 +1115,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
     flower.act_cookie.data = ufid;
     flower.act_cookie.len = sizeof *ufid;
 
-    err = tc_replace_flower(ifindex, prio, handle, &flower);
+    err = tc_replace_flower(ifindex, prio, handle, &flower, block_id);
     if (!err) {
         add_ufid_tc_mapping(ufid, flower.prio, flower.handle, netdev, ifindex);
     }
@@ -1131,6 +1135,7 @@ netdev_tc_flow_get(struct netdev *netdev OVS_UNUSED,
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
     struct netdev *dev;
     struct tc_flower flower;
+    uint32_t block_id = 0;
     odp_port_t in_port;
     int prio = 0;
     int ifindex;
@@ -1152,7 +1157,7 @@ netdev_tc_flow_get(struct netdev *netdev OVS_UNUSED,
 
     VLOG_DBG_RL(&rl, "flow get (dev %s prio %d handle %d)",
                 netdev_get_name(dev), prio, handle);
-    err = tc_get_flower(ifindex, prio, handle, &flower);
+    err = tc_get_flower(ifindex, prio, handle, &flower, block_id);
     netdev_close(dev);
     if (err) {
         VLOG_ERR_RL(&error_rl, "flow get failed (dev %s prio %d handle %d): %s",
@@ -1175,6 +1180,7 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
                    struct dpif_flow_stats *stats)
 {
     struct tc_flower flower;
+    uint32_t block_id = 0;
     struct netdev *dev;
     int prio = 0;
     int ifindex;
@@ -1196,14 +1202,14 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
 
     if (stats) {
         memset(stats, 0, sizeof *stats);
-        if (!tc_get_flower(ifindex, prio, handle, &flower)) {
+        if (!tc_get_flower(ifindex, prio, handle, &flower, block_id)) {
             stats->n_packets = get_32aligned_u64(&flower.stats.n_packets);
             stats->n_bytes = get_32aligned_u64(&flower.stats.n_bytes);
             stats->used = flower.lastused;
         }
     }
 
-    error = tc_del_filter(ifindex, prio, handle);
+    error = tc_del_filter(ifindex, prio, handle, block_id);
     del_ufid_tc_mapping(ufid);
 
     netdev_close(dev);
@@ -1212,7 +1218,7 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
 }
 
 static void
-probe_multi_mask_per_prio(int ifindex)
+probe_multi_mask_per_prio(int ifindex, uint32_t block_id)
 {
     struct tc_flower flower;
     int error;
@@ -1224,7 +1230,7 @@ probe_multi_mask_per_prio(int ifindex)
     memset(&flower.key.dst_mac, 0x11, sizeof flower.key.dst_mac);
     memset(&flower.mask.dst_mac, 0xff, sizeof flower.mask.dst_mac);
 
-    error = tc_replace_flower(ifindex, 1, 1, &flower);
+    error = tc_replace_flower(ifindex, 1, 1, &flower, block_id);
     if (error) {
         return;
     }
@@ -1232,23 +1238,42 @@ probe_multi_mask_per_prio(int ifindex)
     memset(&flower.key.src_mac, 0x11, sizeof flower.key.src_mac);
     memset(&flower.mask.src_mac, 0xff, sizeof flower.mask.src_mac);
 
-    error = tc_replace_flower(ifindex, 1, 2, &flower);
-    tc_del_filter(ifindex, 1, 1);
+    error = tc_replace_flower(ifindex, 1, 2, &flower, block_id);
+    tc_del_filter(ifindex, 1, 1, block_id);
 
     if (error) {
         return;
     }
 
-    tc_del_filter(ifindex, 1, 2);
+    tc_del_filter(ifindex, 1, 2, block_id);
 
     multi_mask_per_prio = true;
     VLOG_INFO("probe tc: multiple masks on single tc prio is supported.");
 }
 
+static void
+probe_tc_block_support(int ifindex)
+{
+    uint32_t block_id = 1;
+    int error;
+
+    error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
+    if (error) {
+        return;
+    }
+
+    tc_add_del_ingress_qdisc(ifindex, false, block_id);
+
+    block_support = true;
+    VLOG_INFO("probe tc: block offload is supported.");
+}
+
 int
 netdev_tc_init_flow_api(struct netdev *netdev)
 {
-    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+    static struct ovsthread_once multi_mask_once = OVSTHREAD_ONCE_INITIALIZER;
+    static struct ovsthread_once block_once = OVSTHREAD_ONCE_INITIALIZER;
+    uint32_t block_id = 0;
     int ifindex;
     int error;
 
@@ -1259,7 +1284,12 @@ netdev_tc_init_flow_api(struct netdev *netdev)
         return -ifindex;
     }
 
-    error = tc_add_del_ingress_qdisc(ifindex, true);
+    if (ovsthread_once_start(&block_once)) {
+        probe_tc_block_support(ifindex);
+        ovsthread_once_done(&block_once);
+    }
+
+    error = tc_add_del_ingress_qdisc(ifindex, true, block_id);
 
     if (error && error != EEXIST) {
         VLOG_ERR("failed adding ingress qdisc required for offloading: %s",
@@ -1269,9 +1299,9 @@ netdev_tc_init_flow_api(struct netdev *netdev)
 
     VLOG_INFO("added ingress qdisc to %s", netdev_get_name(netdev));
 
-    if (ovsthread_once_start(&once)) {
-        probe_multi_mask_per_prio(ifindex);
-        ovsthread_once_done(&once);
+    if (ovsthread_once_start(&multi_mask_once)) {
+        probe_multi_mask_per_prio(ifindex, block_id);
+        ovsthread_once_done(&multi_mask_once);
     }
 
     return 0;
diff --git a/lib/tc.c b/lib/tc.c
index 7133486..7a71036 100644
--- a/lib/tc.c
+++ b/lib/tc.c
@@ -43,6 +43,14 @@
 
 #define MAX_PEDIT_OFFSETS 32
 
+#ifndef TCM_IFINDEX_MAGIC_BLOCK
+#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU)
+#endif
+
+#if TCA_MAX < 14
+#define TCA_INGRESS_BLOCK 13
+#endif
+
 VLOG_DEFINE_THIS_MODULE(tc);
 
 static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
@@ -173,10 +181,14 @@ tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
  * The configuration and stats may be seen with the following command:
  *     /sbin/tc -s qdisc show dev <devname>
  *
+ * If block_id is greater than 0, then the ingress qdisc is added to a block.
+ * In this case, it is equivalent to running (when 'add' is true):
+ *     /sbin/tc qdisc add dev <devname> ingress_block <block_id> ingress
+ *
  * Returns 0 if successful, otherwise a positive errno value.
  */
 int
-tc_add_del_ingress_qdisc(int ifindex, bool add)
+tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
@@ -189,6 +201,9 @@ tc_add_del_ingress_qdisc(int ifindex, bool add)
     tcmsg->tcm_parent = TC_H_INGRESS;
     nl_msg_put_string(&request, TCA_KIND, "ingress");
     nl_msg_put_unspec(&request, TCA_OPTIONS, NULL, 0);
+    if (block_id) {
+        nl_msg_put_u32(&request, TCA_INGRESS_BLOCK, block_id);
+    }
 
     error = tc_transact(&request, NULL);
     if (error) {
@@ -1007,13 +1022,15 @@ parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tc_flower *flower)
 }
 
 int
-tc_dump_flower_start(int ifindex, struct nl_dump *dump)
+tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
+    int index;
 
-    tcmsg = tc_make_request(ifindex, RTM_GETTFILTER, NLM_F_DUMP, &request);
-    tcmsg->tcm_parent = TC_INGRESS_PARENT;
+    index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+    tcmsg = tc_make_request(index, RTM_GETTFILTER, NLM_F_DUMP, &request);
+    tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
     tcmsg->tcm_info = TC_H_UNSPEC;
     tcmsg->tcm_handle = 0;
 
@@ -1024,28 +1041,32 @@ tc_dump_flower_start(int ifindex, struct nl_dump *dump)
 }
 
 int
-tc_flush(int ifindex)
+tc_flush(int ifindex, uint32_t block_id)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
+    int index;
 
-    tcmsg = tc_make_request(ifindex, RTM_DELTFILTER, NLM_F_ACK, &request);
-    tcmsg->tcm_parent = TC_INGRESS_PARENT;
+    index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+    tcmsg = tc_make_request(index, RTM_DELTFILTER, NLM_F_ACK, &request);
+    tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
     tcmsg->tcm_info = TC_H_UNSPEC;
 
     return tc_transact(&request, NULL);
 }
 
 int
-tc_del_filter(int ifindex, int prio, int handle)
+tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
     struct ofpbuf *reply;
     int error;
+    int index;
 
-    tcmsg = tc_make_request(ifindex, RTM_DELTFILTER, NLM_F_ECHO, &request);
-    tcmsg->tcm_parent = TC_INGRESS_PARENT;
+    index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+    tcmsg = tc_make_request(index, RTM_DELTFILTER, NLM_F_ECHO, &request);
+    tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
     tcmsg->tcm_info = tc_make_handle(prio, 0);
     tcmsg->tcm_handle = handle;
 
@@ -1057,15 +1078,18 @@ tc_del_filter(int ifindex, int prio, int handle)
 }
 
 int
-tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower)
+tc_get_flower(int ifindex, int prio, int handle, struct tc_flower *flower,
+              uint32_t block_id)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
     struct ofpbuf *reply;
     int error;
+    int index;
 
-    tcmsg = tc_make_request(ifindex, RTM_GETTFILTER, NLM_F_ECHO, &request);
-    tcmsg->tcm_parent = TC_INGRESS_PARENT;
+    index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+    tcmsg = tc_make_request(index, RTM_GETTFILTER, NLM_F_ECHO, &request);
+    tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
     tcmsg->tcm_info = tc_make_handle(prio, 0);
     tcmsg->tcm_handle = handle;
 
@@ -1625,7 +1649,7 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
 
 int
 tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
-                  struct tc_flower *flower)
+                  struct tc_flower *flower, uint32_t block_id)
 {
     struct ofpbuf request;
     struct tcmsg *tcmsg;
@@ -1633,10 +1657,12 @@ tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
     int error = 0;
     size_t basic_offset;
     uint16_t eth_type = (OVS_FORCE uint16_t) flower->key.eth_type;
+    int index;
 
-    tcmsg = tc_make_request(ifindex, RTM_NEWTFILTER,
-                            NLM_F_CREATE | NLM_F_ECHO, &request);
-    tcmsg->tcm_parent = TC_INGRESS_PARENT;
+    index = block_id ? TCM_IFINDEX_MAGIC_BLOCK : ifindex;
+    tcmsg = tc_make_request(index, RTM_NEWTFILTER, NLM_F_CREATE | NLM_F_ECHO,
+                            &request);
+    tcmsg->tcm_parent = block_id ? : TC_INGRESS_PARENT;
     tcmsg->tcm_info = tc_make_handle(prio, eth_type);
     tcmsg->tcm_handle = handle;
 
diff --git a/lib/tc.h b/lib/tc.h
index cc19374..80505f0 100644
--- a/lib/tc.h
+++ b/lib/tc.h
@@ -63,7 +63,7 @@ tc_get_minor(unsigned int handle)
 struct tcmsg *tc_make_request(int ifindex, int type,
                               unsigned int flags, struct ofpbuf *);
 int tc_transact(struct ofpbuf *request, struct ofpbuf **replyp);
-int tc_add_del_ingress_qdisc(int ifindex, bool add);
+int tc_add_del_ingress_qdisc(int ifindex, bool add, uint32_t block_id);
 
 struct tc_cookie {
     const void *data;
@@ -198,12 +198,12 @@ BUILD_ASSERT_DECL(offsetof(struct tc_flower, rewrite)
                   + sizeof(uint32_t) - 2 < sizeof(struct tc_flower));
 
 int tc_replace_flower(int ifindex, uint16_t prio, uint32_t handle,
-                      struct tc_flower *flower);
-int tc_del_filter(int ifindex, int prio, int handle);
+                      struct tc_flower *flower, uint32_t block_id);
+int tc_del_filter(int ifindex, int prio, int handle, uint32_t block_id);
 int tc_get_flower(int ifindex, int prio, int handle,
-                  struct tc_flower *flower);
-int tc_flush(int ifindex);
-int tc_dump_flower_start(int ifindex, struct nl_dump *dump);
+                  struct tc_flower *flower, uint32_t block_id);
+int tc_flush(int ifindex, uint32_t block_id);
+int tc_dump_flower_start(int ifindex, struct nl_dump *dump, uint32_t block_id);
 int parse_netlink_to_tc_flower(struct ofpbuf *reply,
                                struct tc_flower *flower);
 void tc_set_policy(const char *policy);
