blkg->key = cfqd is an rcu protected pointer and hence we used to docall_rcu(cfqd->rcu_head) to free up cfqd after one rcu grace period.

The problem here is that even though cfqd is around, there are nogurantees that associated request queue (td->queue) or q->queue_lockis still around. A driver might have called blk_cleanup_queue() andrelease the lock.

It might happen that after freeing up the lock we callblkg->key->queue->queue_ock and crash. This is possible in followingpath.

blkiocg_destroy() blkio_unlink_group_fn() cfq_unlink_blkio_group()

Hence, wait for an rcu peirod if there are groups which have notbeen unlinked from blkcg->blkg_list. That way, if there are any groupswhich are taking cfq_unlink_blkio_group() path, can safely take queuelock.

cfq_put_async_queues(cfqd); cfq_release_cfq_groups(cfqd);- cfq_blkiocg_del_blkio_group(&cfqd->root_group.blkg);++ /*+ * If there are groups which we could not unlink from blkcg list,+ * wait for a rcu period for them to be freed.+ */+ if (cfqd->nr_blkcg_linked_grps)+ wait = true;

- /* Wait for cfqg->blkg->key accessors to exit their grace periods. */- call_rcu(&cfqd->rcu, cfq_cfqd_free);+ /*+ * Wait for cfqg->blkg->key accessors to exit their grace periods.+ * Do this wait only if there are other unlinked groups out+ * there. This can happen if cgroup deletion path claimed the+ * responsibility of cleaning up a group before queue cleanup code+ * get to the group.+ *+ * Do not call synchronize_rcu() unconditionally as there are drivers+ * which create/delete request queue hundreds of times during scan/boot+ * and synchronize_rcu() can take significant time and slow down boot.+ */+ if (wait)+ synchronize_rcu();+ kfree(cfqd); }

#ifdef CONFIG_CFQ_GROUP_IOSCHED /*- * Take a reference to root group which we never drop. This is just- * to make sure that cfq_put_cfqg() does not try to kfree root group+ * Set root group reference to 2. One reference will be dropped when+ * all groups on cfqd->cfqg_list are being deleted during queue exit.+ * Other reference will remain there as we don't want to delete this+ * group as it is statically allocated and gets destroyed when+ * throtl_data goes away. */- cfqg->ref = 1;+ cfqg->ref = 2; rcu_read_lock(); cfq_blkiocg_add_blkio_group(&blkio_root_cgroup, &cfqg->blkg, (void *)cfqd, 0); rcu_read_unlock();+ cfqd->nr_blkcg_linked_grps++;++ /* Add group on cfqd->cfqg_list */+ hlist_add_head(&cfqg->cfqd_node, &cfqd->cfqg_list); #endif /* * Not strictly needed (since RB_ROOT just clears the node and we-- 1.7.1