spin_unlock_bh(qdisc_lock(qdisc));
 }
 
-int mlx5e_htb_leaf_del(struct mlx5e_priv *priv, u16 classid, u16 *old_qid,
-                      u16 *new_qid, struct netlink_ext_ack *extack)
+int mlx5e_htb_leaf_del(struct mlx5e_priv *priv, u16 *classid,
+                      struct netlink_ext_ack *extack)
 {
        struct mlx5e_qos_node *node;
        struct netdev_queue *txq;
        bool opened;
        int err;
 
-       qos_dbg(priv->mdev, "TC_HTB_LEAF_DEL classid %04x\n", classid);
-
-       *old_qid = *new_qid = 0;
+       qos_dbg(priv->mdev, "TC_HTB_LEAF_DEL classid %04x\n", *classid);
 
-       node = mlx5e_sw_node_find(priv, classid);
+       node = mlx5e_sw_node_find(priv, *classid);
        if (!node)
                return -ENOENT;
 
        err = mlx5_qos_destroy_node(priv->mdev, node->hw_id);
        if (err) /* Not fatal. */
                qos_warn(priv->mdev, "Failed to destroy leaf node %u (class %04x), err = %d\n",
-                        node->hw_id, classid, err);
+                        node->hw_id, *classid, err);
 
        mlx5e_sw_node_delete(priv, node);
 
        if (opened)
                mlx5e_reactivate_qos_sq(priv, moved_qid, txq);
 
-       *old_qid = mlx5e_qid_from_qos(&priv->channels, moved_qid);
-       *new_qid = mlx5e_qid_from_qos(&priv->channels, qid);
+       *classid = node->classid;
        return 0;
 }
 
 
                struct htb_class_leaf {
                        int             deficit[TC_HTB_MAXDEPTH];
                        struct Qdisc    *q;
+                       struct netdev_queue *offload_queue;
                } leaf;
                struct htb_class_inner {
                        struct htb_prio clprio[TC_HTB_NUMPRIO];
        return old_q;
 }
 
-static void htb_offload_move_qdisc(struct Qdisc *sch, u16 qid_old, u16 qid_new)
+static struct netdev_queue *htb_offload_get_queue(struct htb_class *cl)
+{
+       struct netdev_queue *queue;
+
+       queue = cl->leaf.offload_queue;
+       if (!(cl->leaf.q->flags & TCQ_F_BUILTIN))
+               WARN_ON(cl->leaf.q->dev_queue != queue);
+
+       return queue;
+}
+
+static void htb_offload_move_qdisc(struct Qdisc *sch, struct htb_class *cl_old,
+                                  struct htb_class *cl_new, bool destroying)
 {
        struct netdev_queue *queue_old, *queue_new;
        struct net_device *dev = qdisc_dev(sch);
-       struct Qdisc *qdisc;
 
-       queue_old = netdev_get_tx_queue(dev, qid_old);
-       queue_new = netdev_get_tx_queue(dev, qid_new);
+       queue_old = htb_offload_get_queue(cl_old);
+       queue_new = htb_offload_get_queue(cl_new);
 
-       if (dev->flags & IFF_UP)
-               dev_deactivate(dev);
-       qdisc = dev_graft_qdisc(queue_old, NULL);
-       qdisc->dev_queue = queue_new;
-       qdisc = dev_graft_qdisc(queue_new, qdisc);
-       if (dev->flags & IFF_UP)
-               dev_activate(dev);
+       if (!destroying) {
+               struct Qdisc *qdisc;
 
-       WARN_ON(!(qdisc->flags & TCQ_F_BUILTIN));
+               if (dev->flags & IFF_UP)
+                       dev_deactivate(dev);
+               qdisc = dev_graft_qdisc(queue_old, NULL);
+               WARN_ON(qdisc != cl_old->leaf.q);
+       }
+
+       if (!(cl_old->leaf.q->flags & TCQ_F_BUILTIN))
+               cl_old->leaf.q->dev_queue = queue_new;
+       cl_old->leaf.offload_queue = queue_new;
+
+       if (!destroying) {
+               struct Qdisc *qdisc;
+
+               qdisc = dev_graft_qdisc(queue_new, cl_old->leaf.q);
+               if (dev->flags & IFF_UP)
+                       dev_activate(dev);
+               WARN_ON(!(qdisc->flags & TCQ_F_BUILTIN));
+       }
 }
 
 static int htb_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
        if (cl->level)
                return -EINVAL;
 
-       if (q->offload) {
-               dev_queue = new->dev_queue;
-               WARN_ON(dev_queue != cl->leaf.q->dev_queue);
-       }
+       if (q->offload)
+               dev_queue = htb_offload_get_queue(cl);
 
        if (!new) {
                new = qdisc_create_dflt(dev_queue, &pfifo_qdisc_ops,
        parent->ctokens = parent->cbuffer;
        parent->t_c = ktime_get_ns();
        parent->cmode = HTB_CAN_SEND;
+       if (q->offload)
+               parent->leaf.offload_queue = cl->leaf.offload_queue;
 }
 
 static void htb_parent_to_leaf_offload(struct Qdisc *sch,
                                     struct netlink_ext_ack *extack)
 {
        struct tc_htb_qopt_offload offload_opt;
+       struct netdev_queue *dev_queue;
        struct Qdisc *q = cl->leaf.q;
        struct Qdisc *old = NULL;
        int err;
                return -EINVAL;
 
        WARN_ON(!q);
-       if (!destroying) {
-               /* On destroy of HTB, two cases are possible:
-                * 1. q is a normal qdisc, but q->dev_queue has noop qdisc.
-                * 2. q is a noop qdisc (for nodes that were inner),
-                *    q->dev_queue is noop_netdev_queue.
+       dev_queue = htb_offload_get_queue(cl);
+       old = htb_graft_helper(dev_queue, NULL);
+       if (destroying)
+               /* Before HTB is destroyed, the kernel grafts noop_qdisc to
+                * all queues.
                 */
-               old = htb_graft_helper(q->dev_queue, NULL);
-               WARN_ON(!old);
+               WARN_ON(!(old->flags & TCQ_F_BUILTIN));
+       else
                WARN_ON(old != q);
-       }
 
        if (cl->parent) {
                cl->parent->bstats_bias.bytes += q->bstats.bytes;
        if (!err || destroying)
                qdisc_put(old);
        else
-               htb_graft_helper(q->dev_queue, old);
+               htb_graft_helper(dev_queue, old);
 
        if (last_child)
                return err;
 
-       if (!err && offload_opt.moved_qid != 0) {
-               if (destroying)
-                       q->dev_queue = netdev_get_tx_queue(qdisc_dev(sch),
-                                                          offload_opt.qid);
-               else
-                       htb_offload_move_qdisc(sch, offload_opt.moved_qid,
-                                              offload_opt.qid);
+       if (!err && offload_opt.classid != TC_H_MIN(cl->common.classid)) {
+               u32 classid = TC_H_MAJ(sch->handle) |
+                             TC_H_MIN(offload_opt.classid);
+               struct htb_class *moved_cl = htb_find(classid, sch);
+
+               htb_offload_move_qdisc(sch, moved_cl, cl, destroying);
        }
 
        return err;
        }
 
        if (last_child) {
-               struct netdev_queue *dev_queue;
+               struct netdev_queue *dev_queue = sch->dev_queue;
+
+               if (q->offload)
+                       dev_queue = htb_offload_get_queue(cl);
 
-               dev_queue = q->offload ? cl->leaf.q->dev_queue : sch->dev_queue;
                new_q = qdisc_create_dflt(dev_queue, &pfifo_qdisc_ops,
                                          cl->parent->common.classid,
                                          NULL);
                        }
                        dev_queue = netdev_get_tx_queue(dev, offload_opt.qid);
                } else { /* First child. */
-                       dev_queue = parent->leaf.q->dev_queue;
+                       dev_queue = htb_offload_get_queue(parent);
                        old_q = htb_graft_helper(dev_queue, NULL);
                        WARN_ON(old_q != parent->leaf.q);
                        offload_opt = (struct tc_htb_qopt_offload) {
 
                /* leaf (we) needs elementary qdisc */
                cl->leaf.q = new_q ? new_q : &noop_qdisc;
+               if (q->offload)
+                       cl->leaf.offload_queue = dev_queue;
 
                cl->parent = parent;