pinctrl: at91-pio4: add missing of_node_put
[linux-2.6-block.git] / block / blk-cgroup.c
index c2033a232a443a3b54def7e9d88b646a7ac950d5..1c16694ae14523c144f7be2af43c3c697cc13f7a 100644 (file)
@@ -307,11 +307,28 @@ struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,
        }
 }
 
+static void blkg_pd_offline(struct blkcg_gq *blkg)
+{
+       int i;
+
+       lockdep_assert_held(blkg->q->queue_lock);
+       lockdep_assert_held(&blkg->blkcg->lock);
+
+       for (i = 0; i < BLKCG_MAX_POLS; i++) {
+               struct blkcg_policy *pol = blkcg_policy[i];
+
+               if (blkg->pd[i] && !blkg->pd[i]->offline &&
+                   pol->pd_offline_fn) {
+                       pol->pd_offline_fn(blkg->pd[i]);
+                       blkg->pd[i]->offline = true;
+               }
+       }
+}
+
 static void blkg_destroy(struct blkcg_gq *blkg)
 {
        struct blkcg *blkcg = blkg->blkcg;
        struct blkcg_gq *parent = blkg->parent;
-       int i;
 
        lockdep_assert_held(blkg->q->queue_lock);
        lockdep_assert_held(&blkcg->lock);
@@ -320,13 +337,6 @@ static void blkg_destroy(struct blkcg_gq *blkg)
        WARN_ON_ONCE(list_empty(&blkg->q_node));
        WARN_ON_ONCE(hlist_unhashed(&blkg->blkcg_node));
 
-       for (i = 0; i < BLKCG_MAX_POLS; i++) {
-               struct blkcg_policy *pol = blkcg_policy[i];
-
-               if (blkg->pd[i] && pol->pd_offline_fn)
-                       pol->pd_offline_fn(blkg->pd[i]);
-       }
-
        if (parent) {
                blkg_rwstat_add_aux(&parent->stat_bytes, &blkg->stat_bytes);
                blkg_rwstat_add_aux(&parent->stat_ios, &blkg->stat_ios);
@@ -369,6 +379,7 @@ static void blkg_destroy_all(struct request_queue *q)
                struct blkcg *blkcg = blkg->blkcg;
 
                spin_lock(&blkcg->lock);
+               blkg_pd_offline(blkg);
                blkg_destroy(blkg);
                spin_unlock(&blkcg->lock);
        }
@@ -995,25 +1006,25 @@ static struct cftype blkcg_legacy_files[] = {
  * @css: css of interest
  *
  * This function is called when @css is about to go away and responsible
- * for shooting down all blkgs associated with @css.  blkgs should be
- * removed while holding both q and blkcg locks.  As blkcg lock is nested
- * inside q lock, this function performs reverse double lock dancing.
+ * for offlining all blkgs pd and killing all wbs associated with @css.
+ * blkgs pd offline should be done while holding both q and blkcg locks.
+ * As blkcg lock is nested inside q lock, this function performs reverse
+ * double lock dancing.
  *
  * This is the blkcg counterpart of ioc_release_fn().
  */
 static void blkcg_css_offline(struct cgroup_subsys_state *css)
 {
        struct blkcg *blkcg = css_to_blkcg(css);
+       struct blkcg_gq *blkg;
 
        spin_lock_irq(&blkcg->lock);
 
-       while (!hlist_empty(&blkcg->blkg_list)) {
-               struct blkcg_gq *blkg = hlist_entry(blkcg->blkg_list.first,
-                                               struct blkcg_gq, blkcg_node);
+       hlist_for_each_entry(blkg, &blkcg->blkg_list, blkcg_node) {
                struct request_queue *q = blkg->q;
 
                if (spin_trylock(q->queue_lock)) {
-                       blkg_destroy(blkg);
+                       blkg_pd_offline(blkg);
                        spin_unlock(q->queue_lock);
                } else {
                        spin_unlock_irq(&blkcg->lock);
@@ -1027,11 +1038,43 @@ static void blkcg_css_offline(struct cgroup_subsys_state *css)
        wb_blkcg_offline(blkcg);
 }
 
+/**
+ * blkcg_destroy_all_blkgs - destroy all blkgs associated with a blkcg
+ * @blkcg: blkcg of interest
+ *
+ * This function is called when blkcg css is about to free and responsible for
+ * destroying all blkgs associated with @blkcg.
+ * blkgs should be removed while holding both q and blkcg locks. As blkcg lock
+ * is nested inside q lock, this function performs reverse double lock dancing.
+ */
+static void blkcg_destroy_all_blkgs(struct blkcg *blkcg)
+{
+       spin_lock_irq(&blkcg->lock);
+       while (!hlist_empty(&blkcg->blkg_list)) {
+               struct blkcg_gq *blkg = hlist_entry(blkcg->blkg_list.first,
+                                                   struct blkcg_gq,
+                                                   blkcg_node);
+               struct request_queue *q = blkg->q;
+
+               if (spin_trylock(q->queue_lock)) {
+                       blkg_destroy(blkg);
+                       spin_unlock(q->queue_lock);
+               } else {
+                       spin_unlock_irq(&blkcg->lock);
+                       cpu_relax();
+                       spin_lock_irq(&blkcg->lock);
+               }
+       }
+       spin_unlock_irq(&blkcg->lock);
+}
+
 static void blkcg_css_free(struct cgroup_subsys_state *css)
 {
        struct blkcg *blkcg = css_to_blkcg(css);
        int i;
 
+       blkcg_destroy_all_blkgs(blkcg);
+
        mutex_lock(&blkcg_pol_mutex);
 
        list_del(&blkcg->all_blkcgs_node);
@@ -1371,8 +1414,11 @@ void blkcg_deactivate_policy(struct request_queue *q,
                spin_lock(&blkg->blkcg->lock);
 
                if (blkg->pd[pol->plid]) {
-                       if (pol->pd_offline_fn)
+                       if (!blkg->pd[pol->plid]->offline &&
+                           pol->pd_offline_fn) {
                                pol->pd_offline_fn(blkg->pd[pol->plid]);
+                               blkg->pd[pol->plid]->offline = true;
+                       }
                        pol->pd_free_fn(blkg->pd[pol->plid]);
                        blkg->pd[pol->plid] = NULL;
                }