#include <linux/errno.h>
 #include <linux/skbuff.h>
 #include <net/pkt_sched.h>
+#include <net/pkt_cls.h>
 
 /* 1 band FIFO pseudo-"scheduler" */
 
        return NET_XMIT_CN;
 }
 
-static int fifo_init(struct Qdisc *sch, struct nlattr *opt,
-                    struct netlink_ext_ack *extack)
+static void fifo_offload_init(struct Qdisc *sch)
+{
+       struct net_device *dev = qdisc_dev(sch);
+       struct tc_fifo_qopt_offload qopt;
+
+       if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
+               return;
+
+       qopt.command = TC_FIFO_REPLACE;
+       qopt.handle = sch->handle;
+       qopt.parent = sch->parent;
+       dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_FIFO, &qopt);
+}
+
+static void fifo_offload_destroy(struct Qdisc *sch)
+{
+       struct net_device *dev = qdisc_dev(sch);
+       struct tc_fifo_qopt_offload qopt;
+
+       if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
+               return;
+
+       qopt.command = TC_FIFO_DESTROY;
+       qopt.handle = sch->handle;
+       qopt.parent = sch->parent;
+       dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_FIFO, &qopt);
+}
+
+static int fifo_offload_dump(struct Qdisc *sch)
+{
+       struct tc_fifo_qopt_offload qopt;
+
+       qopt.command = TC_FIFO_STATS;
+       qopt.handle = sch->handle;
+       qopt.parent = sch->parent;
+       qopt.stats.bstats = &sch->bstats;
+       qopt.stats.qstats = &sch->qstats;
+
+       return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_FIFO, &qopt);
+}
+
+static int __fifo_init(struct Qdisc *sch, struct nlattr *opt,
+                      struct netlink_ext_ack *extack)
 {
        bool bypass;
        bool is_bfifo = sch->ops == &bfifo_qdisc_ops;
                sch->flags |= TCQ_F_CAN_BYPASS;
        else
                sch->flags &= ~TCQ_F_CAN_BYPASS;
+
        return 0;
 }
 
-static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
+static int fifo_init(struct Qdisc *sch, struct nlattr *opt,
+                    struct netlink_ext_ack *extack)
+{
+       int err;
+
+       err = __fifo_init(sch, opt, extack);
+       if (err)
+               return err;
+
+       fifo_offload_init(sch);
+       return 0;
+}
+
+static int fifo_hd_init(struct Qdisc *sch, struct nlattr *opt,
+                       struct netlink_ext_ack *extack)
+{
+       return __fifo_init(sch, opt, extack);
+}
+
+static void fifo_destroy(struct Qdisc *sch)
+{
+       fifo_offload_destroy(sch);
+}
+
+static int __fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
        struct tc_fifo_qopt opt = { .limit = sch->limit };
 
        return -1;
 }
 
+static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
+{
+       int err;
+
+       err = fifo_offload_dump(sch);
+       if (err)
+               return err;
+
+       return __fifo_dump(sch, skb);
+}
+
+static int fifo_hd_dump(struct Qdisc *sch, struct sk_buff *skb)
+{
+       return __fifo_dump(sch, skb);
+}
+
 struct Qdisc_ops pfifo_qdisc_ops __read_mostly = {
        .id             =       "pfifo",
        .priv_size      =       0,
        .dequeue        =       qdisc_dequeue_head,
        .peek           =       qdisc_peek_head,
        .init           =       fifo_init,
+       .destroy        =       fifo_destroy,
        .reset          =       qdisc_reset_queue,
        .change         =       fifo_init,
        .dump           =       fifo_dump,
        .dequeue        =       qdisc_dequeue_head,
        .peek           =       qdisc_peek_head,
        .init           =       fifo_init,
+       .destroy        =       fifo_destroy,
        .reset          =       qdisc_reset_queue,
        .change         =       fifo_init,
        .dump           =       fifo_dump,
        .enqueue        =       pfifo_tail_enqueue,
        .dequeue        =       qdisc_dequeue_head,
        .peek           =       qdisc_peek_head,
-       .init           =       fifo_init,
+       .init           =       fifo_hd_init,
        .reset          =       qdisc_reset_queue,
-       .change         =       fifo_init,
-       .dump           =       fifo_dump,
+       .change         =       fifo_hd_init,
+       .dump           =       fifo_hd_dump,
        .owner          =       THIS_MODULE,
 };