del_timer_sync(&q->backing_dev_info->laptop_mode_wb_timer);
        blk_sync_queue(q);
 
+       /*
+        * I/O scheduler exit is only safe after the sysfs scheduler attribute
+        * has been removed.
+        */
+       WARN_ON_ONCE(q->kobj.state_in_sysfs);
+
+       /*
+        * Since the I/O scheduler exit code may access cgroup information,
+        * perform I/O scheduler exit before disassociating from the block
+        * cgroup controller.
+        */
+       if (q->elevator) {
+               ioc_clear_queue(q);
+               elevator_exit(q, q->elevator);
+               q->elevator = NULL;
+       }
+
+       /*
+        * Remove all references to @q from the block cgroup controller before
+        * restoring @q->queue_lock to avoid that restoring this pointer causes
+        * e.g. blkcg_print_blkgs() to crash.
+        */
+       blkcg_exit_queue(q);
+
+       /*
+        * Since the cgroup code may dereference the @q->backing_dev_info
+        * pointer, only decrease its reference count after having removed the
+        * association with the block cgroup controller.
+        */
+       bdi_put(q->backing_dev_info);
+
        if (q->mq_ops)
                blk_mq_free_queue(q);
        percpu_ref_exit(&q->q_usage_counter);
 
        if (test_bit(QUEUE_FLAG_POLL_STATS, &q->queue_flags))
                blk_stat_remove_callback(q, q->poll_cb);
        blk_stat_free_callback(q->poll_cb);
-       bdi_put(q->backing_dev_info);
-       blkcg_exit_queue(q);
-
-       if (q->elevator) {
-               ioc_clear_queue(q);
-               elevator_exit(q, q->elevator);
-       }
 
        blk_free_queue_stats(q->stats);