blkcg: move blkio_group_conf->weight to cfq
[sfrench/cifs-2.6.git] / block / blk-cgroup.c
index ea84a23d5e680d8acc8dfc67948e5872f0142ba7..c0e239b8455ec019abe5084fc7a31e9315a3e3f1 100644 (file)
  *                   Nauman Rafique <nauman@google.com>
  */
 #include <linux/ioprio.h>
-#include <linux/seq_file.h>
 #include <linux/kdev_t.h>
 #include <linux/module.h>
 #include <linux/err.h>
 #include <linux/blkdev.h>
 #include <linux/slab.h>
-#include "blk-cgroup.h"
 #include <linux/genhd.h>
+#include <linux/delay.h>
+#include <linux/atomic.h>
+#include "blk-cgroup.h"
+#include "blk.h"
 
 #define MAX_KEY_LEN 100
 
 static DEFINE_SPINLOCK(blkio_list_lock);
 static LIST_HEAD(blkio_list);
 
-struct blkio_cgroup blkio_root_cgroup = { .weight = 2*BLKIO_WEIGHT_DEFAULT };
-EXPORT_SYMBOL_GPL(blkio_root_cgroup);
-
-static struct cgroup_subsys_state *blkiocg_create(struct cgroup *);
-static int blkiocg_can_attach(struct cgroup *, struct cgroup_taskset *);
-static void blkiocg_attach(struct cgroup *, struct cgroup_taskset *);
-static void blkiocg_destroy(struct cgroup *);
-static int blkiocg_populate(struct cgroup_subsys *, struct cgroup *);
-
-/* for encoding cft->private value on file */
-#define BLKIOFILE_PRIVATE(x, val)      (((x) << 16) | (val))
-/* What policy owns the file, proportional or throttle */
-#define BLKIOFILE_POLICY(val)          (((val) >> 16) & 0xffff)
-#define BLKIOFILE_ATTR(val)            ((val) & 0xffff)
-
-struct cgroup_subsys blkio_subsys = {
-       .name = "blkio",
-       .create = blkiocg_create,
-       .can_attach = blkiocg_can_attach,
-       .attach = blkiocg_attach,
-       .destroy = blkiocg_destroy,
-       .populate = blkiocg_populate,
-#ifdef CONFIG_BLK_CGROUP
-       /* note: blkio_subsys_id is otherwise defined in blk-cgroup.h */
-       .subsys_id = blkio_subsys_id,
-#endif
-       .use_id = 1,
-       .module = THIS_MODULE,
-};
-EXPORT_SYMBOL_GPL(blkio_subsys);
-
-static inline void blkio_policy_insert_node(struct blkio_cgroup *blkcg,
-                                           struct blkio_policy_node *pn)
-{
-       list_add(&pn->node, &blkcg->policy_list);
-}
-
-static inline bool cftype_blkg_same_policy(struct cftype *cft,
-                       struct blkio_group *blkg)
-{
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-
-       if (blkg->plid == plid)
-               return 1;
-
-       return 0;
-}
-
-/* Determines if policy node matches cgroup file being accessed */
-static inline bool pn_matches_cftype(struct cftype *cft,
-                       struct blkio_policy_node *pn)
-{
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-       int fileid = BLKIOFILE_ATTR(cft->private);
-
-       return (plid == pn->plid && fileid == pn->fileid);
-}
-
-/* Must be called with blkcg->lock held */
-static inline void blkio_policy_delete_node(struct blkio_policy_node *pn)
-{
-       list_del(&pn->node);
-}
-
-/* Must be called with blkcg->lock held */
-static struct blkio_policy_node *
-blkio_policy_search_node(const struct blkio_cgroup *blkcg, dev_t dev,
-               enum blkio_policy_id plid, int fileid)
-{
-       struct blkio_policy_node *pn;
+static DEFINE_MUTEX(all_q_mutex);
+static LIST_HEAD(all_q_list);
 
-       list_for_each_entry(pn, &blkcg->policy_list, node) {
-               if (pn->dev == dev && pn->plid == plid && pn->fileid == fileid)
-                       return pn;
-       }
+struct blkio_cgroup blkio_root_cgroup = { .cfq_weight = 2 * CFQ_WEIGHT_DEFAULT };
+EXPORT_SYMBOL_GPL(blkio_root_cgroup);
 
-       return NULL;
-}
+static struct blkio_policy_type *blkio_policy[BLKIO_NR_POLICIES];
 
 struct blkio_cgroup *cgroup_to_blkio_cgroup(struct cgroup *cgroup)
 {
@@ -111,1492 +42,563 @@ struct blkio_cgroup *cgroup_to_blkio_cgroup(struct cgroup *cgroup)
 }
 EXPORT_SYMBOL_GPL(cgroup_to_blkio_cgroup);
 
-struct blkio_cgroup *task_blkio_cgroup(struct task_struct *tsk)
+static struct blkio_cgroup *task_blkio_cgroup(struct task_struct *tsk)
 {
        return container_of(task_subsys_state(tsk, blkio_subsys_id),
                            struct blkio_cgroup, css);
 }
-EXPORT_SYMBOL_GPL(task_blkio_cgroup);
 
-static inline void
-blkio_update_group_weight(struct blkio_group *blkg, unsigned int weight)
+struct blkio_cgroup *bio_blkio_cgroup(struct bio *bio)
 {
-       struct blkio_policy_type *blkiop;
-
-       list_for_each_entry(blkiop, &blkio_list, list) {
-               /* If this policy does not own the blkg, do not send updates */
-               if (blkiop->plid != blkg->plid)
-                       continue;
-               if (blkiop->ops.blkio_update_group_weight_fn)
-                       blkiop->ops.blkio_update_group_weight_fn(blkg->key,
-                                                       blkg, weight);
-       }
+       if (bio && bio->bi_css)
+               return container_of(bio->bi_css, struct blkio_cgroup, css);
+       return task_blkio_cgroup(current);
 }
+EXPORT_SYMBOL_GPL(bio_blkio_cgroup);
 
-static inline void blkio_update_group_bps(struct blkio_group *blkg, u64 bps,
-                               int fileid)
+/**
+ * blkg_free - free a blkg
+ * @blkg: blkg to free
+ *
+ * Free @blkg which may be partially allocated.
+ */
+static void blkg_free(struct blkio_group *blkg)
 {
-       struct blkio_policy_type *blkiop;
-
-       list_for_each_entry(blkiop, &blkio_list, list) {
-
-               /* If this policy does not own the blkg, do not send updates */
-               if (blkiop->plid != blkg->plid)
-                       continue;
-
-               if (fileid == BLKIO_THROTL_read_bps_device
-                   && blkiop->ops.blkio_update_group_read_bps_fn)
-                       blkiop->ops.blkio_update_group_read_bps_fn(blkg->key,
-                                                               blkg, bps);
-
-               if (fileid == BLKIO_THROTL_write_bps_device
-                   && blkiop->ops.blkio_update_group_write_bps_fn)
-                       blkiop->ops.blkio_update_group_write_bps_fn(blkg->key,
-                                                               blkg, bps);
-       }
-}
+       int i;
 
-static inline void blkio_update_group_iops(struct blkio_group *blkg,
-                       unsigned int iops, int fileid)
-{
-       struct blkio_policy_type *blkiop;
+       if (!blkg)
+               return;
 
-       list_for_each_entry(blkiop, &blkio_list, list) {
+       for (i = 0; i < BLKIO_NR_POLICIES; i++) {
+               struct blkio_policy_type *pol = blkio_policy[i];
+               struct blkg_policy_data *pd = blkg->pd[i];
 
-               /* If this policy does not own the blkg, do not send updates */
-               if (blkiop->plid != blkg->plid)
+               if (!pd)
                        continue;
 
-               if (fileid == BLKIO_THROTL_read_iops_device
-                   && blkiop->ops.blkio_update_group_read_iops_fn)
-                       blkiop->ops.blkio_update_group_read_iops_fn(blkg->key,
-                                                               blkg, iops);
+               if (pol && pol->ops.blkio_exit_group_fn)
+                       pol->ops.blkio_exit_group_fn(blkg);
 
-               if (fileid == BLKIO_THROTL_write_iops_device
-                   && blkiop->ops.blkio_update_group_write_iops_fn)
-                       blkiop->ops.blkio_update_group_write_iops_fn(blkg->key,
-                                                               blkg,iops);
+               kfree(pd);
        }
-}
 
-/*
- * Add to the appropriate stat variable depending on the request type.
- * This should be called with the blkg->stats_lock held.
- */
-static void blkio_add_stat(uint64_t *stat, uint64_t add, bool direction,
-                               bool sync)
-{
-       if (direction)
-               stat[BLKIO_STAT_WRITE] += add;
-       else
-               stat[BLKIO_STAT_READ] += add;
-       if (sync)
-               stat[BLKIO_STAT_SYNC] += add;
-       else
-               stat[BLKIO_STAT_ASYNC] += add;
+       kfree(blkg);
 }
 
-/*
- * Decrements the appropriate stat variable if non-zero depending on the
- * request type. Panics on value being zero.
- * This should be called with the blkg->stats_lock held.
+/**
+ * blkg_alloc - allocate a blkg
+ * @blkcg: block cgroup the new blkg is associated with
+ * @q: request_queue the new blkg is associated with
+ *
+ * Allocate a new blkg assocating @blkcg and @q.
  */
-static void blkio_check_and_dec_stat(uint64_t *stat, bool direction, bool sync)
-{
-       if (direction) {
-               BUG_ON(stat[BLKIO_STAT_WRITE] == 0);
-               stat[BLKIO_STAT_WRITE]--;
-       } else {
-               BUG_ON(stat[BLKIO_STAT_READ] == 0);
-               stat[BLKIO_STAT_READ]--;
-       }
-       if (sync) {
-               BUG_ON(stat[BLKIO_STAT_SYNC] == 0);
-               stat[BLKIO_STAT_SYNC]--;
-       } else {
-               BUG_ON(stat[BLKIO_STAT_ASYNC] == 0);
-               stat[BLKIO_STAT_ASYNC]--;
-       }
-}
-
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-/* This should be called with the blkg->stats_lock held. */
-static void blkio_set_start_group_wait_time(struct blkio_group *blkg,
-                                               struct blkio_group *curr_blkg)
-{
-       if (blkio_blkg_waiting(&blkg->stats))
-               return;
-       if (blkg == curr_blkg)
-               return;
-       blkg->stats.start_group_wait_time = sched_clock();
-       blkio_mark_blkg_waiting(&blkg->stats);
-}
-
-/* This should be called with the blkg->stats_lock held. */
-static void blkio_update_group_wait_time(struct blkio_group_stats *stats)
+static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg,
+                                     struct request_queue *q)
 {
-       unsigned long long now;
-
-       if (!blkio_blkg_waiting(stats))
-               return;
-
-       now = sched_clock();
-       if (time_after64(now, stats->start_group_wait_time))
-               stats->group_wait_time += now - stats->start_group_wait_time;
-       blkio_clear_blkg_waiting(stats);
-}
+       struct blkio_group *blkg;
+       int i;
 
-/* This should be called with the blkg->stats_lock held. */
-static void blkio_end_empty_time(struct blkio_group_stats *stats)
-{
-       unsigned long long now;
+       /* alloc and init base part */
+       blkg = kzalloc_node(sizeof(*blkg), GFP_ATOMIC, q->node);
+       if (!blkg)
+               return NULL;
 
-       if (!blkio_blkg_empty(stats))
-               return;
+       blkg->q = q;
+       INIT_LIST_HEAD(&blkg->q_node);
+       blkg->blkcg = blkcg;
+       blkg->refcnt = 1;
+       cgroup_path(blkcg->css.cgroup, blkg->path, sizeof(blkg->path));
 
-       now = sched_clock();
-       if (time_after64(now, stats->start_empty_time))
-               stats->empty_time += now - stats->start_empty_time;
-       blkio_clear_blkg_empty(stats);
-}
+       for (i = 0; i < BLKIO_NR_POLICIES; i++) {
+               struct blkio_policy_type *pol = blkio_policy[i];
+               struct blkg_policy_data *pd;
 
-void blkiocg_update_set_idle_time_stats(struct blkio_group *blkg)
-{
-       unsigned long flags;
+               if (!pol)
+                       continue;
 
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       BUG_ON(blkio_blkg_idling(&blkg->stats));
-       blkg->stats.start_idle_time = sched_clock();
-       blkio_mark_blkg_idling(&blkg->stats);
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_set_idle_time_stats);
+               /* alloc per-policy data and attach it to blkg */
+               pd = kzalloc_node(sizeof(*pd) + pol->pdata_size, GFP_ATOMIC,
+                                 q->node);
+               if (!pd) {
+                       blkg_free(blkg);
+                       return NULL;
+               }
 
-void blkiocg_update_idle_time_stats(struct blkio_group *blkg)
-{
-       unsigned long flags;
-       unsigned long long now;
-       struct blkio_group_stats *stats;
-
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       stats = &blkg->stats;
-       if (blkio_blkg_idling(stats)) {
-               now = sched_clock();
-               if (time_after64(now, stats->start_idle_time))
-                       stats->idle_time += now - stats->start_idle_time;
-               blkio_clear_blkg_idling(stats);
+               blkg->pd[i] = pd;
+               pd->blkg = blkg;
        }
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_idle_time_stats);
-
-void blkiocg_update_avg_queue_size_stats(struct blkio_group *blkg)
-{
-       unsigned long flags;
-       struct blkio_group_stats *stats;
-
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       stats = &blkg->stats;
-       stats->avg_queue_size_sum +=
-                       stats->stat_arr[BLKIO_STAT_QUEUED][BLKIO_STAT_READ] +
-                       stats->stat_arr[BLKIO_STAT_QUEUED][BLKIO_STAT_WRITE];
-       stats->avg_queue_size_samples++;
-       blkio_update_group_wait_time(stats);
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_avg_queue_size_stats);
-
-void blkiocg_set_start_empty_time(struct blkio_group *blkg)
-{
-       unsigned long flags;
-       struct blkio_group_stats *stats;
-
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       stats = &blkg->stats;
 
-       if (stats->stat_arr[BLKIO_STAT_QUEUED][BLKIO_STAT_READ] ||
-                       stats->stat_arr[BLKIO_STAT_QUEUED][BLKIO_STAT_WRITE]) {
-               spin_unlock_irqrestore(&blkg->stats_lock, flags);
-               return;
-       }
+       /* invoke per-policy init */
+       for (i = 0; i < BLKIO_NR_POLICIES; i++) {
+               struct blkio_policy_type *pol = blkio_policy[i];
 
-       /*
-        * group is already marked empty. This can happen if cfqq got new
-        * request in parent group and moved to this group while being added
-        * to service tree. Just ignore the event and move on.
-        */
-       if(blkio_blkg_empty(stats)) {
-               spin_unlock_irqrestore(&blkg->stats_lock, flags);
-               return;
+               if (pol)
+                       pol->ops.blkio_init_group_fn(blkg);
        }
 
-       stats->start_empty_time = sched_clock();
-       blkio_mark_blkg_empty(stats);
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
+       return blkg;
 }
-EXPORT_SYMBOL_GPL(blkiocg_set_start_empty_time);
 
-void blkiocg_update_dequeue_stats(struct blkio_group *blkg,
-                       unsigned long dequeue)
-{
-       blkg->stats.dequeue += dequeue;
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_dequeue_stats);
-#else
-static inline void blkio_set_start_group_wait_time(struct blkio_group *blkg,
-                                       struct blkio_group *curr_blkg) {}
-static inline void blkio_end_empty_time(struct blkio_group_stats *stats) {}
-#endif
-
-void blkiocg_update_io_add_stats(struct blkio_group *blkg,
-                       struct blkio_group *curr_blkg, bool direction,
-                       bool sync)
+struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg,
+                                      struct request_queue *q,
+                                      bool for_root)
+       __releases(q->queue_lock) __acquires(q->queue_lock)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       blkio_add_stat(blkg->stats.stat_arr[BLKIO_STAT_QUEUED], 1, direction,
-                       sync);
-       blkio_end_empty_time(&blkg->stats);
-       blkio_set_start_group_wait_time(blkg, curr_blkg);
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_io_add_stats);
-
-void blkiocg_update_io_remove_stats(struct blkio_group *blkg,
-                                               bool direction, bool sync)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       blkio_check_and_dec_stat(blkg->stats.stat_arr[BLKIO_STAT_QUEUED],
-                                       direction, sync);
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_io_remove_stats);
-
-void blkiocg_update_timeslice_used(struct blkio_group *blkg, unsigned long time,
-                               unsigned long unaccounted_time)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       blkg->stats.time += time;
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-       blkg->stats.unaccounted_time += unaccounted_time;
-#endif
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_timeslice_used);
+       struct blkio_group *blkg;
 
-/*
- * should be called under rcu read lock or queue lock to make sure blkg pointer
- * is valid.
- */
-void blkiocg_update_dispatch_stats(struct blkio_group *blkg,
-                               uint64_t bytes, bool direction, bool sync)
-{
-       struct blkio_group_stats_cpu *stats_cpu;
-       unsigned long flags;
+       WARN_ON_ONCE(!rcu_read_lock_held());
+       lockdep_assert_held(q->queue_lock);
 
        /*
-        * Disabling interrupts to provide mutual exclusion between two
-        * writes on same cpu. It probably is not needed for 64bit. Not
-        * optimizing that case yet.
+        * This could be the first entry point of blkcg implementation and
+        * we shouldn't allow anything to go through for a bypassing queue.
+        * The following can be removed if blkg lookup is guaranteed to
+        * fail on a bypassing queue.
         */
-       local_irq_save(flags);
-
-       stats_cpu = this_cpu_ptr(blkg->stats_cpu);
-
-       u64_stats_update_begin(&stats_cpu->syncp);
-       stats_cpu->sectors += bytes >> 9;
-       blkio_add_stat(stats_cpu->stat_arr_cpu[BLKIO_STAT_CPU_SERVICED],
-                       1, direction, sync);
-       blkio_add_stat(stats_cpu->stat_arr_cpu[BLKIO_STAT_CPU_SERVICE_BYTES],
-                       bytes, direction, sync);
-       u64_stats_update_end(&stats_cpu->syncp);
-       local_irq_restore(flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_dispatch_stats);
+       if (unlikely(blk_queue_bypass(q)) && !for_root)
+               return ERR_PTR(blk_queue_dead(q) ? -EINVAL : -EBUSY);
 
-void blkiocg_update_completion_stats(struct blkio_group *blkg,
-       uint64_t start_time, uint64_t io_start_time, bool direction, bool sync)
-{
-       struct blkio_group_stats *stats;
-       unsigned long flags;
-       unsigned long long now = sched_clock();
-
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       stats = &blkg->stats;
-       if (time_after64(now, io_start_time))
-               blkio_add_stat(stats->stat_arr[BLKIO_STAT_SERVICE_TIME],
-                               now - io_start_time, direction, sync);
-       if (time_after64(io_start_time, start_time))
-               blkio_add_stat(stats->stat_arr[BLKIO_STAT_WAIT_TIME],
-                               io_start_time - start_time, direction, sync);
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_completion_stats);
+       blkg = blkg_lookup(blkcg, q);
+       if (blkg)
+               return blkg;
 
-/*  Merged stats are per cpu.  */
-void blkiocg_update_io_merged_stats(struct blkio_group *blkg, bool direction,
-                                       bool sync)
-{
-       struct blkio_group_stats_cpu *stats_cpu;
-       unsigned long flags;
+       /* blkg holds a reference to blkcg */
+       if (!css_tryget(&blkcg->css))
+               return ERR_PTR(-EINVAL);
 
        /*
-        * Disabling interrupts to provide mutual exclusion between two
-        * writes on same cpu. It probably is not needed for 64bit. Not
-        * optimizing that case yet.
+        * Allocate and initialize.
         */
-       local_irq_save(flags);
+       blkg = blkg_alloc(blkcg, q);
 
-       stats_cpu = this_cpu_ptr(blkg->stats_cpu);
-
-       u64_stats_update_begin(&stats_cpu->syncp);
-       blkio_add_stat(stats_cpu->stat_arr_cpu[BLKIO_STAT_CPU_MERGED], 1,
-                               direction, sync);
-       u64_stats_update_end(&stats_cpu->syncp);
-       local_irq_restore(flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_io_merged_stats);
-
-/*
- * This function allocates the per cpu stats for blkio_group. Should be called
- * from sleepable context as alloc_per_cpu() requires that.
- */
-int blkio_alloc_blkg_stats(struct blkio_group *blkg)
-{
-       /* Allocate memory for per cpu stats */
-       blkg->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu);
-       if (!blkg->stats_cpu)
-               return -ENOMEM;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(blkio_alloc_blkg_stats);
-
-void blkiocg_add_blkio_group(struct blkio_cgroup *blkcg,
-               struct blkio_group *blkg, void *key, dev_t dev,
-               enum blkio_policy_id plid)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&blkcg->lock, flags);
-       spin_lock_init(&blkg->stats_lock);
-       rcu_assign_pointer(blkg->key, key);
-       blkg->blkcg_id = css_id(&blkcg->css);
-       hlist_add_head_rcu(&blkg->blkcg_node, &blkcg->blkg_list);
-       blkg->plid = plid;
-       spin_unlock_irqrestore(&blkcg->lock, flags);
-       /* Need to take css reference ? */
-       cgroup_path(blkcg->css.cgroup, blkg->path, sizeof(blkg->path));
-       blkg->dev = dev;
-}
-EXPORT_SYMBOL_GPL(blkiocg_add_blkio_group);
-
-static void __blkiocg_del_blkio_group(struct blkio_group *blkg)
-{
-       hlist_del_init_rcu(&blkg->blkcg_node);
-       blkg->blkcg_id = 0;
-}
-
-/*
- * returns 0 if blkio_group was still on cgroup list. Otherwise returns 1
- * indicating that blk_group was unhashed by the time we got to it.
- */
-int blkiocg_del_blkio_group(struct blkio_group *blkg)
-{
-       struct blkio_cgroup *blkcg;
-       unsigned long flags;
-       struct cgroup_subsys_state *css;
-       int ret = 1;
-
-       rcu_read_lock();
-       css = css_lookup(&blkio_subsys, blkg->blkcg_id);
-       if (css) {
-               blkcg = container_of(css, struct blkio_cgroup, css);
-               spin_lock_irqsave(&blkcg->lock, flags);
-               if (!hlist_unhashed(&blkg->blkcg_node)) {
-                       __blkiocg_del_blkio_group(blkg);
-                       ret = 0;
-               }
-               spin_unlock_irqrestore(&blkcg->lock, flags);
+       /* did alloc fail? */
+       if (unlikely(!blkg)) {
+               blkg = ERR_PTR(-ENOMEM);
+               goto out;
        }
 
-       rcu_read_unlock();
-       return ret;
+       /* insert */
+       spin_lock(&blkcg->lock);
+       hlist_add_head_rcu(&blkg->blkcg_node, &blkcg->blkg_list);
+       list_add(&blkg->q_node, &q->blkg_list);
+       spin_unlock(&blkcg->lock);
+out:
+       return blkg;
 }
-EXPORT_SYMBOL_GPL(blkiocg_del_blkio_group);
+EXPORT_SYMBOL_GPL(blkg_lookup_create);
 
 /* called under rcu_read_lock(). */
-struct blkio_group *blkiocg_lookup_group(struct blkio_cgroup *blkcg, void *key)
+struct blkio_group *blkg_lookup(struct blkio_cgroup *blkcg,
+                               struct request_queue *q)
 {
        struct blkio_group *blkg;
        struct hlist_node *n;
-       void *__key;
 
-       hlist_for_each_entry_rcu(blkg, n, &blkcg->blkg_list, blkcg_node) {
-               __key = blkg->key;
-               if (__key == key)
+       hlist_for_each_entry_rcu(blkg, n, &blkcg->blkg_list, blkcg_node)
+               if (blkg->q == q)
                        return blkg;
-       }
-
        return NULL;
 }
-EXPORT_SYMBOL_GPL(blkiocg_lookup_group);
+EXPORT_SYMBOL_GPL(blkg_lookup);
 
-static void blkio_reset_stats_cpu(struct blkio_group *blkg)
+static void blkg_destroy(struct blkio_group *blkg)
 {
-       struct blkio_group_stats_cpu *stats_cpu;
-       int i, j, k;
+       struct request_queue *q = blkg->q;
+       struct blkio_cgroup *blkcg = blkg->blkcg;
+
+       lockdep_assert_held(q->queue_lock);
+       lockdep_assert_held(&blkcg->lock);
+
+       /* Something wrong if we are trying to remove same group twice */
+       WARN_ON_ONCE(list_empty(&blkg->q_node));
+       WARN_ON_ONCE(hlist_unhashed(&blkg->blkcg_node));
+       list_del_init(&blkg->q_node);
+       hlist_del_init_rcu(&blkg->blkcg_node);
+
        /*
-        * Note: On 64 bit arch this should not be an issue. This has the
-        * possibility of returning some inconsistent value on 32bit arch
-        * as 64bit update on 32bit is non atomic. Taking care of this
-        * corner case makes code very complicated, like sending IPIs to
-        * cpus, taking care of stats of offline cpus etc.
-        *
-        * reset stats is anyway more of a debug feature and this sounds a
-        * corner case. So I am not complicating the code yet until and
-        * unless this becomes a real issue.
+        * Put the reference taken at the time of creation so that when all
+        * queues are gone, group can be destroyed.
         */
-       for_each_possible_cpu(i) {
-               stats_cpu = per_cpu_ptr(blkg->stats_cpu, i);
-               stats_cpu->sectors = 0;
-               for(j = 0; j < BLKIO_STAT_CPU_NR; j++)
-                       for (k = 0; k < BLKIO_STAT_TOTAL; k++)
-                               stats_cpu->stat_arr_cpu[j][k] = 0;
-       }
+       blkg_put(blkg);
 }
 
-static int
-blkiocg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val)
+/*
+ * XXX: This updates blkg policy data in-place for root blkg, which is
+ * necessary across elevator switch and policy registration as root blkgs
+ * aren't shot down.  This broken and racy implementation is temporary.
+ * Eventually, blkg shoot down will be replaced by proper in-place update.
+ */
+void update_root_blkg_pd(struct request_queue *q, enum blkio_policy_id plid)
 {
-       struct blkio_cgroup *blkcg;
-       struct blkio_group *blkg;
-       struct blkio_group_stats *stats;
-       struct hlist_node *n;
-       uint64_t queued[BLKIO_STAT_TOTAL];
-       int i;
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-       bool idling, waiting, empty;
-       unsigned long long now = sched_clock();
-#endif
-
-       blkcg = cgroup_to_blkio_cgroup(cgroup);
-       spin_lock_irq(&blkcg->lock);
-       hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) {
-               spin_lock(&blkg->stats_lock);
-               stats = &blkg->stats;
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-               idling = blkio_blkg_idling(stats);
-               waiting = blkio_blkg_waiting(stats);
-               empty = blkio_blkg_empty(stats);
-#endif
-               for (i = 0; i < BLKIO_STAT_TOTAL; i++)
-                       queued[i] = stats->stat_arr[BLKIO_STAT_QUEUED][i];
-               memset(stats, 0, sizeof(struct blkio_group_stats));
-               for (i = 0; i < BLKIO_STAT_TOTAL; i++)
-                       stats->stat_arr[BLKIO_STAT_QUEUED][i] = queued[i];
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-               if (idling) {
-                       blkio_mark_blkg_idling(stats);
-                       stats->start_idle_time = now;
-               }
-               if (waiting) {
-                       blkio_mark_blkg_waiting(stats);
-                       stats->start_group_wait_time = now;
-               }
-               if (empty) {
-                       blkio_mark_blkg_empty(stats);
-                       stats->start_empty_time = now;
-               }
-#endif
-               spin_unlock(&blkg->stats_lock);
+       struct blkio_policy_type *pol = blkio_policy[plid];
+       struct blkio_group *blkg = blkg_lookup(&blkio_root_cgroup, q);
+       struct blkg_policy_data *pd;
 
-               /* Reset Per cpu stats which don't take blkg->stats_lock */
-               blkio_reset_stats_cpu(blkg);
-       }
+       if (!blkg)
+               return;
 
-       spin_unlock_irq(&blkcg->lock);
-       return 0;
-}
+       kfree(blkg->pd[plid]);
+       blkg->pd[plid] = NULL;
 
-static void blkio_get_key_name(enum stat_sub_type type, dev_t dev, char *str,
-                               int chars_left, bool diskname_only)
-{
-       snprintf(str, chars_left, "%d:%d", MAJOR(dev), MINOR(dev));
-       chars_left -= strlen(str);
-       if (chars_left <= 0) {
-               printk(KERN_WARNING
-                       "Possibly incorrect cgroup stat display format");
+       if (!pol)
                return;
-       }
-       if (diskname_only)
-               return;
-       switch (type) {
-       case BLKIO_STAT_READ:
-               strlcat(str, " Read", chars_left);
-               break;
-       case BLKIO_STAT_WRITE:
-               strlcat(str, " Write", chars_left);
-               break;
-       case BLKIO_STAT_SYNC:
-               strlcat(str, " Sync", chars_left);
-               break;
-       case BLKIO_STAT_ASYNC:
-               strlcat(str, " Async", chars_left);
-               break;
-       case BLKIO_STAT_TOTAL:
-               strlcat(str, " Total", chars_left);
-               break;
-       default:
-               strlcat(str, " Invalid", chars_left);
-       }
-}
-
-static uint64_t blkio_fill_stat(char *str, int chars_left, uint64_t val,
-                               struct cgroup_map_cb *cb, dev_t dev)
-{
-       blkio_get_key_name(0, dev, str, chars_left, true);
-       cb->fill(cb, str, val);
-       return val;
-}
 
+       pd = kzalloc(sizeof(*pd) + pol->pdata_size, GFP_KERNEL);
+       WARN_ON_ONCE(!pd);
 
-static uint64_t blkio_read_stat_cpu(struct blkio_group *blkg,
-                       enum stat_type_cpu type, enum stat_sub_type sub_type)
-{
-       int cpu;
-       struct blkio_group_stats_cpu *stats_cpu;
-       u64 val = 0, tval;
-
-       for_each_possible_cpu(cpu) {
-               unsigned int start;
-               stats_cpu  = per_cpu_ptr(blkg->stats_cpu, cpu);
-
-               do {
-                       start = u64_stats_fetch_begin(&stats_cpu->syncp);
-                       if (type == BLKIO_STAT_CPU_SECTORS)
-                               tval = stats_cpu->sectors;
-                       else
-                               tval = stats_cpu->stat_arr_cpu[type][sub_type];
-               } while(u64_stats_fetch_retry(&stats_cpu->syncp, start));
-
-               val += tval;
-       }
-
-       return val;
+       blkg->pd[plid] = pd;
+       pd->blkg = blkg;
+       pol->ops.blkio_init_group_fn(blkg);
 }
+EXPORT_SYMBOL_GPL(update_root_blkg_pd);
 
-static uint64_t blkio_get_stat_cpu(struct blkio_group *blkg,
-               struct cgroup_map_cb *cb, dev_t dev, enum stat_type_cpu type)
+/**
+ * blkg_destroy_all - destroy all blkgs associated with a request_queue
+ * @q: request_queue of interest
+ * @destroy_root: whether to destroy root blkg or not
+ *
+ * Destroy blkgs associated with @q.  If @destroy_root is %true, all are
+ * destroyed; otherwise, root blkg is left alone.
+ */
+void blkg_destroy_all(struct request_queue *q, bool destroy_root)
 {
-       uint64_t disk_total, val;
-       char key_str[MAX_KEY_LEN];
-       enum stat_sub_type sub_type;
+       struct blkio_group *blkg, *n;
 
-       if (type == BLKIO_STAT_CPU_SECTORS) {
-               val = blkio_read_stat_cpu(blkg, type, 0);
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1, val, cb, dev);
-       }
-
-       for (sub_type = BLKIO_STAT_READ; sub_type < BLKIO_STAT_TOTAL;
-                       sub_type++) {
-               blkio_get_key_name(sub_type, dev, key_str, MAX_KEY_LEN, false);
-               val = blkio_read_stat_cpu(blkg, type, sub_type);
-               cb->fill(cb, key_str, val);
-       }
+       spin_lock_irq(q->queue_lock);
 
-       disk_total = blkio_read_stat_cpu(blkg, type, BLKIO_STAT_READ) +
-                       blkio_read_stat_cpu(blkg, type, BLKIO_STAT_WRITE);
+       list_for_each_entry_safe(blkg, n, &q->blkg_list, q_node) {
+               struct blkio_cgroup *blkcg = blkg->blkcg;
 
-       blkio_get_key_name(BLKIO_STAT_TOTAL, dev, key_str, MAX_KEY_LEN, false);
-       cb->fill(cb, key_str, disk_total);
-       return disk_total;
-}
-
-/* This should be called with blkg->stats_lock held */
-static uint64_t blkio_get_stat(struct blkio_group *blkg,
-               struct cgroup_map_cb *cb, dev_t dev, enum stat_type type)
-{
-       uint64_t disk_total;
-       char key_str[MAX_KEY_LEN];
-       enum stat_sub_type sub_type;
-
-       if (type == BLKIO_STAT_TIME)
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1,
-                                       blkg->stats.time, cb, dev);
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-       if (type == BLKIO_STAT_UNACCOUNTED_TIME)
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1,
-                                       blkg->stats.unaccounted_time, cb, dev);
-       if (type == BLKIO_STAT_AVG_QUEUE_SIZE) {
-               uint64_t sum = blkg->stats.avg_queue_size_sum;
-               uint64_t samples = blkg->stats.avg_queue_size_samples;
-               if (samples)
-                       do_div(sum, samples);
-               else
-                       sum = 0;
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1, sum, cb, dev);
-       }
-       if (type == BLKIO_STAT_GROUP_WAIT_TIME)
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1,
-                                       blkg->stats.group_wait_time, cb, dev);
-       if (type == BLKIO_STAT_IDLE_TIME)
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1,
-                                       blkg->stats.idle_time, cb, dev);
-       if (type == BLKIO_STAT_EMPTY_TIME)
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1,
-                                       blkg->stats.empty_time, cb, dev);
-       if (type == BLKIO_STAT_DEQUEUE)
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1,
-                                       blkg->stats.dequeue, cb, dev);
-#endif
-
-       for (sub_type = BLKIO_STAT_READ; sub_type < BLKIO_STAT_TOTAL;
-                       sub_type++) {
-               blkio_get_key_name(sub_type, dev, key_str, MAX_KEY_LEN, false);
-               cb->fill(cb, key_str, blkg->stats.stat_arr[type][sub_type]);
-       }
-       disk_total = blkg->stats.stat_arr[type][BLKIO_STAT_READ] +
-                       blkg->stats.stat_arr[type][BLKIO_STAT_WRITE];
-       blkio_get_key_name(BLKIO_STAT_TOTAL, dev, key_str, MAX_KEY_LEN, false);
-       cb->fill(cb, key_str, disk_total);
-       return disk_total;
-}
-
-static int blkio_policy_parse_and_set(char *buf,
-       struct blkio_policy_node *newpn, enum blkio_policy_id plid, int fileid)
-{
-       struct gendisk *disk = NULL;
-       char *s[4], *p, *major_s = NULL, *minor_s = NULL;
-       unsigned long major, minor;
-       int i = 0, ret = -EINVAL;
-       int part;
-       dev_t dev;
-       u64 temp;
-
-       memset(s, 0, sizeof(s));
-
-       while ((p = strsep(&buf, " ")) != NULL) {
-               if (!*p)
+               /* skip root? */
+               if (!destroy_root && blkg->blkcg == &blkio_root_cgroup)
                        continue;
 
-               s[i++] = p;
-
-               /* Prevent from inputing too many things */
-               if (i == 3)
-                       break;
+               spin_lock(&blkcg->lock);
+               blkg_destroy(blkg);
+               spin_unlock(&blkcg->lock);
        }
 
-       if (i != 2)
-               goto out;
-
-       p = strsep(&s[0], ":");
-       if (p != NULL)
-               major_s = p;
-       else
-               goto out;
-
-       minor_s = s[0];
-       if (!minor_s)
-               goto out;
-
-       if (strict_strtoul(major_s, 10, &major))
-               goto out;
-
-       if (strict_strtoul(minor_s, 10, &minor))
-               goto out;
-
-       dev = MKDEV(major, minor);
-
-       if (strict_strtoull(s[1], 10, &temp))
-               goto out;
-
-       /* For rule removal, do not check for device presence. */
-       if (temp) {
-               disk = get_gendisk(dev, &part);
-               if (!disk || part) {
-                       ret = -ENODEV;
-                       goto out;
-               }
-       }
-
-       newpn->dev = dev;
-
-       switch (plid) {
-       case BLKIO_POLICY_PROP:
-               if ((temp < BLKIO_WEIGHT_MIN && temp > 0) ||
-                    temp > BLKIO_WEIGHT_MAX)
-                       goto out;
-
-               newpn->plid = plid;
-               newpn->fileid = fileid;
-               newpn->val.weight = temp;
-               break;
-       case BLKIO_POLICY_THROTL:
-               switch(fileid) {
-               case BLKIO_THROTL_read_bps_device:
-               case BLKIO_THROTL_write_bps_device:
-                       newpn->plid = plid;
-                       newpn->fileid = fileid;
-                       newpn->val.bps = temp;
-                       break;
-               case BLKIO_THROTL_read_iops_device:
-               case BLKIO_THROTL_write_iops_device:
-                       if (temp > THROTL_IOPS_MAX)
-                               goto out;
-
-                       newpn->plid = plid;
-                       newpn->fileid = fileid;
-                       newpn->val.iops = (unsigned int)temp;
-                       break;
-               }
-               break;
-       default:
-               BUG();
-       }
-       ret = 0;
-out:
-       put_disk(disk);
-       return ret;
+       spin_unlock_irq(q->queue_lock);
 }
+EXPORT_SYMBOL_GPL(blkg_destroy_all);
 
-unsigned int blkcg_get_weight(struct blkio_cgroup *blkcg,
-                             dev_t dev)
+static void blkg_rcu_free(struct rcu_head *rcu_head)
 {
-       struct blkio_policy_node *pn;
-       unsigned long flags;
-       unsigned int weight;
-
-       spin_lock_irqsave(&blkcg->lock, flags);
-
-       pn = blkio_policy_search_node(blkcg, dev, BLKIO_POLICY_PROP,
-                               BLKIO_PROP_weight_device);
-       if (pn)
-               weight = pn->val.weight;
-       else
-               weight = blkcg->weight;
-
-       spin_unlock_irqrestore(&blkcg->lock, flags);
-
-       return weight;
+       blkg_free(container_of(rcu_head, struct blkio_group, rcu_head));
 }
-EXPORT_SYMBOL_GPL(blkcg_get_weight);
 
-uint64_t blkcg_get_read_bps(struct blkio_cgroup *blkcg, dev_t dev)
+void __blkg_release(struct blkio_group *blkg)
 {
-       struct blkio_policy_node *pn;
-       unsigned long flags;
-       uint64_t bps = -1;
-
-       spin_lock_irqsave(&blkcg->lock, flags);
-       pn = blkio_policy_search_node(blkcg, dev, BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_read_bps_device);
-       if (pn)
-               bps = pn->val.bps;
-       spin_unlock_irqrestore(&blkcg->lock, flags);
-
-       return bps;
-}
+       /* release the extra blkcg reference this blkg has been holding */
+       css_put(&blkg->blkcg->css);
 
-uint64_t blkcg_get_write_bps(struct blkio_cgroup *blkcg, dev_t dev)
-{
-       struct blkio_policy_node *pn;
-       unsigned long flags;
-       uint64_t bps = -1;
-
-       spin_lock_irqsave(&blkcg->lock, flags);
-       pn = blkio_policy_search_node(blkcg, dev, BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_write_bps_device);
-       if (pn)
-               bps = pn->val.bps;
-       spin_unlock_irqrestore(&blkcg->lock, flags);
-
-       return bps;
+       /*
+        * A group is freed in rcu manner. But having an rcu lock does not
+        * mean that one can access all the fields of blkg and assume these
+        * are valid. For example, don't try to follow throtl_data and
+        * request queue links.
+        *
+        * Having a reference to blkg under an rcu allows acess to only
+        * values local to groups like group stats and group rate limits
+        */
+       call_rcu(&blkg->rcu_head, blkg_rcu_free);
 }
+EXPORT_SYMBOL_GPL(__blkg_release);
 
-unsigned int blkcg_get_read_iops(struct blkio_cgroup *blkcg, dev_t dev)
+static int
+blkiocg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val)
 {
-       struct blkio_policy_node *pn;
-       unsigned long flags;
-       unsigned int iops = -1;
-
-       spin_lock_irqsave(&blkcg->lock, flags);
-       pn = blkio_policy_search_node(blkcg, dev, BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_read_iops_device);
-       if (pn)
-               iops = pn->val.iops;
-       spin_unlock_irqrestore(&blkcg->lock, flags);
-
-       return iops;
-}
+       struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgroup);
+       struct blkio_group *blkg;
+       struct hlist_node *n;
 
-unsigned int blkcg_get_write_iops(struct blkio_cgroup *blkcg, dev_t dev)
-{
-       struct blkio_policy_node *pn;
-       unsigned long flags;
-       unsigned int iops = -1;
-
-       spin_lock_irqsave(&blkcg->lock, flags);
-       pn = blkio_policy_search_node(blkcg, dev, BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_write_iops_device);
-       if (pn)
-               iops = pn->val.iops;
-       spin_unlock_irqrestore(&blkcg->lock, flags);
-
-       return iops;
-}
+       spin_lock(&blkio_list_lock);
+       spin_lock_irq(&blkcg->lock);
 
-/* Checks whether user asked for deleting a policy rule */
-static bool blkio_delete_rule_command(struct blkio_policy_node *pn)
-{
-       switch(pn->plid) {
-       case BLKIO_POLICY_PROP:
-               if (pn->val.weight == 0)
-                       return 1;
-               break;
-       case BLKIO_POLICY_THROTL:
-               switch(pn->fileid) {
-               case BLKIO_THROTL_read_bps_device:
-               case BLKIO_THROTL_write_bps_device:
-                       if (pn->val.bps == 0)
-                               return 1;
-                       break;
-               case BLKIO_THROTL_read_iops_device:
-               case BLKIO_THROTL_write_iops_device:
-                       if (pn->val.iops == 0)
-                               return 1;
-               }
-               break;
-       default:
-               BUG();
+       /*
+        * Note that stat reset is racy - it doesn't synchronize against
+        * stat updates.  This is a debug feature which shouldn't exist
+        * anyway.  If you get hit by a race, retry.
+        */
+       hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) {
+               struct blkio_policy_type *pol;
+
+               list_for_each_entry(pol, &blkio_list, list)
+                       if (pol->ops.blkio_reset_group_stats_fn)
+                               pol->ops.blkio_reset_group_stats_fn(blkg);
        }
 
+       spin_unlock_irq(&blkcg->lock);
+       spin_unlock(&blkio_list_lock);
        return 0;
 }
 
-static void blkio_update_policy_rule(struct blkio_policy_node *oldpn,
-                                       struct blkio_policy_node *newpn)
-{
-       switch(oldpn->plid) {
-       case BLKIO_POLICY_PROP:
-               oldpn->val.weight = newpn->val.weight;
-               break;
-       case BLKIO_POLICY_THROTL:
-               switch(newpn->fileid) {
-               case BLKIO_THROTL_read_bps_device:
-               case BLKIO_THROTL_write_bps_device:
-                       oldpn->val.bps = newpn->val.bps;
-                       break;
-               case BLKIO_THROTL_read_iops_device:
-               case BLKIO_THROTL_write_iops_device:
-                       oldpn->val.iops = newpn->val.iops;
-               }
-               break;
-       default:
-               BUG();
-       }
-}
-
-/*
- * Some rules/values in blkg have changed. Propagate those to respective
- * policies.
- */
-static void blkio_update_blkg_policy(struct blkio_cgroup *blkcg,
-               struct blkio_group *blkg, struct blkio_policy_node *pn)
+static const char *blkg_dev_name(struct blkio_group *blkg)
 {
-       unsigned int weight, iops;
-       u64 bps;
-
-       switch(pn->plid) {
-       case BLKIO_POLICY_PROP:
-               weight = pn->val.weight ? pn->val.weight :
-                               blkcg->weight;
-               blkio_update_group_weight(blkg, weight);
-               break;
-       case BLKIO_POLICY_THROTL:
-               switch(pn->fileid) {
-               case BLKIO_THROTL_read_bps_device:
-               case BLKIO_THROTL_write_bps_device:
-                       bps = pn->val.bps ? pn->val.bps : (-1);
-                       blkio_update_group_bps(blkg, bps, pn->fileid);
-                       break;
-               case BLKIO_THROTL_read_iops_device:
-               case BLKIO_THROTL_write_iops_device:
-                       iops = pn->val.iops ? pn->val.iops : (-1);
-                       blkio_update_group_iops(blkg, iops, pn->fileid);
-                       break;
-               }
-               break;
-       default:
-               BUG();
-       }
+       /* some drivers (floppy) instantiate a queue w/o disk registered */
+       if (blkg->q->backing_dev_info.dev)
+               return dev_name(blkg->q->backing_dev_info.dev);
+       return NULL;
 }
 
-/*
- * A policy node rule has been updated. Propagate this update to all the
- * block groups which might be affected by this update.
+/**
+ * blkcg_print_blkgs - helper for printing per-blkg data
+ * @sf: seq_file to print to
+ * @blkcg: blkcg of interest
+ * @prfill: fill function to print out a blkg
+ * @pol: policy in question
+ * @data: data to be passed to @prfill
+ * @show_total: to print out sum of prfill return values or not
+ *
+ * This function invokes @prfill on each blkg of @blkcg if pd for the
+ * policy specified by @pol exists.  @prfill is invoked with @sf, the
+ * policy data and @data.  If @show_total is %true, the sum of the return
+ * values from @prfill is printed with "Total" label at the end.
+ *
+ * This is to be used to construct print functions for
+ * cftype->read_seq_string method.
  */
-static void blkio_update_policy_node_blkg(struct blkio_cgroup *blkcg,
-                               struct blkio_policy_node *pn)
+void blkcg_print_blkgs(struct seq_file *sf, struct blkio_cgroup *blkcg,
+                      u64 (*prfill)(struct seq_file *, struct blkg_policy_data *, int),
+                      int pol, int data, bool show_total)
 {
        struct blkio_group *blkg;
        struct hlist_node *n;
+       u64 total = 0;
 
-       spin_lock(&blkio_list_lock);
        spin_lock_irq(&blkcg->lock);
-
-       hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) {
-               if (pn->dev != blkg->dev || pn->plid != blkg->plid)
-                       continue;
-               blkio_update_blkg_policy(blkcg, blkg, pn);
-       }
-
+       hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node)
+               if (blkg->pd[pol])
+                       total += prfill(sf, blkg->pd[pol], data);
        spin_unlock_irq(&blkcg->lock);
-       spin_unlock(&blkio_list_lock);
+
+       if (show_total)
+               seq_printf(sf, "Total %llu\n", (unsigned long long)total);
 }
+EXPORT_SYMBOL_GPL(blkcg_print_blkgs);
 
-static int blkiocg_file_write(struct cgroup *cgrp, struct cftype *cft,
-                                      const char *buffer)
+/**
+ * __blkg_prfill_u64 - prfill helper for a single u64 value
+ * @sf: seq_file to print to
+ * @pd: policy data of interest
+ * @v: value to print
+ *
+ * Print @v to @sf for the device assocaited with @pd.
+ */
+u64 __blkg_prfill_u64(struct seq_file *sf, struct blkg_policy_data *pd, u64 v)
 {
-       int ret = 0;
-       char *buf;
-       struct blkio_policy_node *newpn, *pn;
-       struct blkio_cgroup *blkcg;
-       int keep_newpn = 0;
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-       int fileid = BLKIOFILE_ATTR(cft->private);
-
-       buf = kstrdup(buffer, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       newpn = kzalloc(sizeof(*newpn), GFP_KERNEL);
-       if (!newpn) {
-               ret = -ENOMEM;
-               goto free_buf;
-       }
+       const char *dname = blkg_dev_name(pd->blkg);
 
-       ret = blkio_policy_parse_and_set(buf, newpn, plid, fileid);
-       if (ret)
-               goto free_newpn;
-
-       blkcg = cgroup_to_blkio_cgroup(cgrp);
-
-       spin_lock_irq(&blkcg->lock);
-
-       pn = blkio_policy_search_node(blkcg, newpn->dev, plid, fileid);
-       if (!pn) {
-               if (!blkio_delete_rule_command(newpn)) {
-                       blkio_policy_insert_node(blkcg, newpn);
-                       keep_newpn = 1;
-               }
-               spin_unlock_irq(&blkcg->lock);
-               goto update_io_group;
-       }
+       if (!dname)
+               return 0;
 
-       if (blkio_delete_rule_command(newpn)) {
-               blkio_policy_delete_node(pn);
-               kfree(pn);
-               spin_unlock_irq(&blkcg->lock);
-               goto update_io_group;
-       }
-       spin_unlock_irq(&blkcg->lock);
+       seq_printf(sf, "%s %llu\n", dname, (unsigned long long)v);
+       return v;
+}
+EXPORT_SYMBOL_GPL(__blkg_prfill_u64);
 
-       blkio_update_policy_rule(pn, newpn);
+/**
+ * __blkg_prfill_rwstat - prfill helper for a blkg_rwstat
+ * @sf: seq_file to print to
+ * @pd: policy data of interest
+ * @rwstat: rwstat to print
+ *
+ * Print @rwstat to @sf for the device assocaited with @pd.
+ */
+u64 __blkg_prfill_rwstat(struct seq_file *sf, struct blkg_policy_data *pd,
+                        const struct blkg_rwstat *rwstat)
+{
+       static const char *rwstr[] = {
+               [BLKG_RWSTAT_READ]      = "Read",
+               [BLKG_RWSTAT_WRITE]     = "Write",
+               [BLKG_RWSTAT_SYNC]      = "Sync",
+               [BLKG_RWSTAT_ASYNC]     = "Async",
+       };
+       const char *dname = blkg_dev_name(pd->blkg);
+       u64 v;
+       int i;
 
-update_io_group:
-       blkio_update_policy_node_blkg(blkcg, newpn);
+       if (!dname)
+               return 0;
 
-free_newpn:
-       if (!keep_newpn)
-               kfree(newpn);
-free_buf:
-       kfree(buf);
-       return ret;
-}
+       for (i = 0; i < BLKG_RWSTAT_NR; i++)
+               seq_printf(sf, "%s %s %llu\n", dname, rwstr[i],
+                          (unsigned long long)rwstat->cnt[i]);
 
-static void
-blkio_print_policy_node(struct seq_file *m, struct blkio_policy_node *pn)
-{
-       switch(pn->plid) {
-               case BLKIO_POLICY_PROP:
-                       if (pn->fileid == BLKIO_PROP_weight_device)
-                               seq_printf(m, "%u:%u\t%u\n", MAJOR(pn->dev),
-                                       MINOR(pn->dev), pn->val.weight);
-                       break;
-               case BLKIO_POLICY_THROTL:
-                       switch(pn->fileid) {
-                       case BLKIO_THROTL_read_bps_device:
-                       case BLKIO_THROTL_write_bps_device:
-                               seq_printf(m, "%u:%u\t%llu\n", MAJOR(pn->dev),
-                                       MINOR(pn->dev), pn->val.bps);
-                               break;
-                       case BLKIO_THROTL_read_iops_device:
-                       case BLKIO_THROTL_write_iops_device:
-                               seq_printf(m, "%u:%u\t%u\n", MAJOR(pn->dev),
-                                       MINOR(pn->dev), pn->val.iops);
-                               break;
-                       }
-                       break;
-               default:
-                       BUG();
-       }
+       v = rwstat->cnt[BLKG_RWSTAT_READ] + rwstat->cnt[BLKG_RWSTAT_WRITE];
+       seq_printf(sf, "%s Total %llu\n", dname, (unsigned long long)v);
+       return v;
 }
 
-/* cgroup files which read their data from policy nodes end up here */
-static void blkio_read_policy_node_files(struct cftype *cft,
-                       struct blkio_cgroup *blkcg, struct seq_file *m)
+static u64 blkg_prfill_stat(struct seq_file *sf, struct blkg_policy_data *pd,
+                           int off)
 {
-       struct blkio_policy_node *pn;
-
-       if (!list_empty(&blkcg->policy_list)) {
-               spin_lock_irq(&blkcg->lock);
-               list_for_each_entry(pn, &blkcg->policy_list, node) {
-                       if (!pn_matches_cftype(cft, pn))
-                               continue;
-                       blkio_print_policy_node(m, pn);
-               }
-               spin_unlock_irq(&blkcg->lock);
-       }
+       return __blkg_prfill_u64(sf, pd,
+                                blkg_stat_read((void *)pd->pdata + off));
 }
 
-static int blkiocg_file_read(struct cgroup *cgrp, struct cftype *cft,
-                               struct seq_file *m)
+static u64 blkg_prfill_rwstat(struct seq_file *sf, struct blkg_policy_data *pd,
+                             int off)
 {
-       struct blkio_cgroup *blkcg;
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-       int name = BLKIOFILE_ATTR(cft->private);
-
-       blkcg = cgroup_to_blkio_cgroup(cgrp);
-
-       switch(plid) {
-       case BLKIO_POLICY_PROP:
-               switch(name) {
-               case BLKIO_PROP_weight_device:
-                       blkio_read_policy_node_files(cft, blkcg, m);
-                       return 0;
-               default:
-                       BUG();
-               }
-               break;
-       case BLKIO_POLICY_THROTL:
-               switch(name){
-               case BLKIO_THROTL_read_bps_device:
-               case BLKIO_THROTL_write_bps_device:
-               case BLKIO_THROTL_read_iops_device:
-               case BLKIO_THROTL_write_iops_device:
-                       blkio_read_policy_node_files(cft, blkcg, m);
-                       return 0;
-               default:
-                       BUG();
-               }
-               break;
-       default:
-               BUG();
-       }
+       struct blkg_rwstat rwstat = blkg_rwstat_read((void *)pd->pdata + off);
 
-       return 0;
+       return __blkg_prfill_rwstat(sf, pd, &rwstat);
 }
 
-static int blkio_read_blkg_stats(struct blkio_cgroup *blkcg,
-               struct cftype *cft, struct cgroup_map_cb *cb,
-               enum stat_type type, bool show_total, bool pcpu)
+/* print blkg_stat specified by BLKCG_STAT_PRIV() */
+int blkcg_print_stat(struct cgroup *cgrp, struct cftype *cft,
+                    struct seq_file *sf)
 {
-       struct blkio_group *blkg;
-       struct hlist_node *n;
-       uint64_t cgroup_total = 0;
+       struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgrp);
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(blkg, n, &blkcg->blkg_list, blkcg_node) {
-               if (blkg->dev) {
-                       if (!cftype_blkg_same_policy(cft, blkg))
-                               continue;
-                       if (pcpu)
-                               cgroup_total += blkio_get_stat_cpu(blkg, cb,
-                                               blkg->dev, type);
-                       else {
-                               spin_lock_irq(&blkg->stats_lock);
-                               cgroup_total += blkio_get_stat(blkg, cb,
-                                               blkg->dev, type);
-                               spin_unlock_irq(&blkg->stats_lock);
-                       }
-               }
-       }
-       if (show_total)
-               cb->fill(cb, "Total", cgroup_total);
-       rcu_read_unlock();
+       blkcg_print_blkgs(sf, blkcg, blkg_prfill_stat,
+                         BLKCG_STAT_POL(cft->private),
+                         BLKCG_STAT_OFF(cft->private), false);
        return 0;
 }
+EXPORT_SYMBOL_GPL(blkcg_print_stat);
 
-/* All map kind of cgroup file get serviced by this function */
-static int blkiocg_file_read_map(struct cgroup *cgrp, struct cftype *cft,
-                               struct cgroup_map_cb *cb)
+/* print blkg_rwstat specified by BLKCG_STAT_PRIV() */
+int blkcg_print_rwstat(struct cgroup *cgrp, struct cftype *cft,
+                      struct seq_file *sf)
 {
-       struct blkio_cgroup *blkcg;
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-       int name = BLKIOFILE_ATTR(cft->private);
-
-       blkcg = cgroup_to_blkio_cgroup(cgrp);
-
-       switch(plid) {
-       case BLKIO_POLICY_PROP:
-               switch(name) {
-               case BLKIO_PROP_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_TIME, 0, 0);
-               case BLKIO_PROP_sectors:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_CPU_SECTORS, 0, 1);
-               case BLKIO_PROP_io_service_bytes:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                       BLKIO_STAT_CPU_SERVICE_BYTES, 1, 1);
-               case BLKIO_PROP_io_serviced:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_CPU_SERVICED, 1, 1);
-               case BLKIO_PROP_io_service_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_SERVICE_TIME, 1, 0);
-               case BLKIO_PROP_io_wait_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_WAIT_TIME, 1, 0);
-               case BLKIO_PROP_io_merged:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_CPU_MERGED, 1, 1);
-               case BLKIO_PROP_io_queued:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_QUEUED, 1, 0);
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-               case BLKIO_PROP_unaccounted_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                       BLKIO_STAT_UNACCOUNTED_TIME, 0, 0);
-               case BLKIO_PROP_dequeue:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_DEQUEUE, 0, 0);
-               case BLKIO_PROP_avg_queue_size:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                       BLKIO_STAT_AVG_QUEUE_SIZE, 0, 0);
-               case BLKIO_PROP_group_wait_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                       BLKIO_STAT_GROUP_WAIT_TIME, 0, 0);
-               case BLKIO_PROP_idle_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_IDLE_TIME, 0, 0);
-               case BLKIO_PROP_empty_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_EMPTY_TIME, 0, 0);
-#endif
-               default:
-                       BUG();
-               }
-               break;
-       case BLKIO_POLICY_THROTL:
-               switch(name){
-               case BLKIO_THROTL_io_service_bytes:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_CPU_SERVICE_BYTES, 1, 1);
-               case BLKIO_THROTL_io_serviced:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_CPU_SERVICED, 1, 1);
-               default:
-                       BUG();
-               }
-               break;
-       default:
-               BUG();
-       }
+       struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgrp);
 
+       blkcg_print_blkgs(sf, blkcg, blkg_prfill_rwstat,
+                         BLKCG_STAT_POL(cft->private),
+                         BLKCG_STAT_OFF(cft->private), true);
        return 0;
 }
+EXPORT_SYMBOL_GPL(blkcg_print_rwstat);
 
-static int blkio_weight_write(struct blkio_cgroup *blkcg, u64 val)
+/**
+ * blkg_conf_prep - parse and prepare for per-blkg config update
+ * @blkcg: target block cgroup
+ * @input: input string
+ * @ctx: blkg_conf_ctx to be filled
+ *
+ * Parse per-blkg config update from @input and initialize @ctx with the
+ * result.  @ctx->blkg points to the blkg to be updated and @ctx->v the new
+ * value.  This function returns with RCU read locked and must be paired
+ * with blkg_conf_finish().
+ */
+int blkg_conf_prep(struct blkio_cgroup *blkcg, const char *input,
+                  struct blkg_conf_ctx *ctx)
+       __acquires(rcu)
 {
+       struct gendisk *disk;
        struct blkio_group *blkg;
-       struct hlist_node *n;
-       struct blkio_policy_node *pn;
+       unsigned int major, minor;
+       unsigned long long v;
+       int part, ret;
 
-       if (val < BLKIO_WEIGHT_MIN || val > BLKIO_WEIGHT_MAX)
+       if (sscanf(input, "%u:%u %llu", &major, &minor, &v) != 3)
                return -EINVAL;
 
-       spin_lock(&blkio_list_lock);
-       spin_lock_irq(&blkcg->lock);
-       blkcg->weight = (unsigned int)val;
-
-       hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) {
-               pn = blkio_policy_search_node(blkcg, blkg->dev,
-                               BLKIO_POLICY_PROP, BLKIO_PROP_weight_device);
-               if (pn)
-                       continue;
-
-               blkio_update_group_weight(blkg, blkcg->weight);
-       }
-       spin_unlock_irq(&blkcg->lock);
-       spin_unlock(&blkio_list_lock);
-       return 0;
-}
+       disk = get_gendisk(MKDEV(major, minor), &part);
+       if (!disk || part)
+               return -EINVAL;
 
-static u64 blkiocg_file_read_u64 (struct cgroup *cgrp, struct cftype *cft) {
-       struct blkio_cgroup *blkcg;
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-       int name = BLKIOFILE_ATTR(cft->private);
+       rcu_read_lock();
 
-       blkcg = cgroup_to_blkio_cgroup(cgrp);
+       spin_lock_irq(disk->queue->queue_lock);
+       blkg = blkg_lookup_create(blkcg, disk->queue, false);
+       spin_unlock_irq(disk->queue->queue_lock);
 
-       switch(plid) {
-       case BLKIO_POLICY_PROP:
-               switch(name) {
-               case BLKIO_PROP_weight:
-                       return (u64)blkcg->weight;
+       if (IS_ERR(blkg)) {
+               ret = PTR_ERR(blkg);
+               rcu_read_unlock();
+               put_disk(disk);
+               /*
+                * If queue was bypassing, we should retry.  Do so after a
+                * short msleep().  It isn't strictly necessary but queue
+                * can be bypassing for some time and it's always nice to
+                * avoid busy looping.
+                */
+               if (ret == -EBUSY) {
+                       msleep(10);
+                       ret = restart_syscall();
                }
-               break;
-       default:
-               BUG();
+               return ret;
        }
+
+       ctx->disk = disk;
+       ctx->blkg = blkg;
+       ctx->v = v;
        return 0;
 }
+EXPORT_SYMBOL_GPL(blkg_conf_prep);
 
-static int
-blkiocg_file_write_u64(struct cgroup *cgrp, struct cftype *cft, u64 val)
+/**
+ * blkg_conf_finish - finish up per-blkg config update
+ * @ctx: blkg_conf_ctx intiailized by blkg_conf_prep()
+ *
+ * Finish up after per-blkg config update.  This function must be paired
+ * with blkg_conf_prep().
+ */
+void blkg_conf_finish(struct blkg_conf_ctx *ctx)
+       __releases(rcu)
 {
-       struct blkio_cgroup *blkcg;
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-       int name = BLKIOFILE_ATTR(cft->private);
-
-       blkcg = cgroup_to_blkio_cgroup(cgrp);
-
-       switch(plid) {
-       case BLKIO_POLICY_PROP:
-               switch(name) {
-               case BLKIO_PROP_weight:
-                       return blkio_weight_write(blkcg, val);
-               }
-               break;
-       default:
-               BUG();
-       }
-
-       return 0;
+       rcu_read_unlock();
+       put_disk(ctx->disk);
 }
+EXPORT_SYMBOL_GPL(blkg_conf_finish);
 
 struct cftype blkio_files[] = {
-       {
-               .name = "weight_device",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_weight_device),
-               .read_seq_string = blkiocg_file_read,
-               .write_string = blkiocg_file_write,
-               .max_write_len = 256,
-       },
-       {
-               .name = "weight",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_weight),
-               .read_u64 = blkiocg_file_read_u64,
-               .write_u64 = blkiocg_file_write_u64,
-       },
-       {
-               .name = "time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_time),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "sectors",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_sectors),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "io_service_bytes",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_io_service_bytes),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "io_serviced",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_io_serviced),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "io_service_time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_io_service_time),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "io_wait_time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_io_wait_time),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "io_merged",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_io_merged),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "io_queued",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_io_queued),
-               .read_map = blkiocg_file_read_map,
-       },
        {
                .name = "reset_stats",
                .write_u64 = blkiocg_reset_stats,
        },
-#ifdef CONFIG_BLK_DEV_THROTTLING
-       {
-               .name = "throttle.read_bps_device",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_read_bps_device),
-               .read_seq_string = blkiocg_file_read,
-               .write_string = blkiocg_file_write,
-               .max_write_len = 256,
-       },
-
-       {
-               .name = "throttle.write_bps_device",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_write_bps_device),
-               .read_seq_string = blkiocg_file_read,
-               .write_string = blkiocg_file_write,
-               .max_write_len = 256,
-       },
-
-       {
-               .name = "throttle.read_iops_device",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_read_iops_device),
-               .read_seq_string = blkiocg_file_read,
-               .write_string = blkiocg_file_write,
-               .max_write_len = 256,
-       },
-
-       {
-               .name = "throttle.write_iops_device",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_write_iops_device),
-               .read_seq_string = blkiocg_file_read,
-               .write_string = blkiocg_file_write,
-               .max_write_len = 256,
-       },
-       {
-               .name = "throttle.io_service_bytes",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_io_service_bytes),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "throttle.io_serviced",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_io_serviced),
-               .read_map = blkiocg_file_read_map,
-       },
-#endif /* CONFIG_BLK_DEV_THROTTLING */
-
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-       {
-               .name = "avg_queue_size",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_avg_queue_size),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "group_wait_time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_group_wait_time),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "idle_time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_idle_time),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "empty_time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_empty_time),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "dequeue",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_dequeue),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "unaccounted_time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_unaccounted_time),
-               .read_map = blkiocg_file_read_map,
-       },
-#endif
+       { }     /* terminate */
 };
 
-static int blkiocg_populate(struct cgroup_subsys *subsys, struct cgroup *cgroup)
-{
-       return cgroup_add_files(cgroup, subsys, blkio_files,
-                               ARRAY_SIZE(blkio_files));
-}
-
-static void blkiocg_destroy(struct cgroup *cgroup)
+/**
+ * blkiocg_pre_destroy - cgroup pre_destroy callback
+ * @cgroup: cgroup of interest
+ *
+ * This function is called when @cgroup is about to go away and responsible
+ * for shooting down all blkgs associated with @cgroup.  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.
+ *
+ * This is the blkcg counterpart of ioc_release_fn().
+ */
+static int blkiocg_pre_destroy(struct cgroup *cgroup)
 {
        struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgroup);
-       unsigned long flags;
-       struct blkio_group *blkg;
-       void *key;
-       struct blkio_policy_type *blkiop;
-       struct blkio_policy_node *pn, *pntmp;
 
-       rcu_read_lock();
-       do {
-               spin_lock_irqsave(&blkcg->lock, flags);
+       spin_lock_irq(&blkcg->lock);
 
-               if (hlist_empty(&blkcg->blkg_list)) {
-                       spin_unlock_irqrestore(&blkcg->lock, flags);
-                       break;
+       while (!hlist_empty(&blkcg->blkg_list)) {
+               struct blkio_group *blkg = hlist_entry(blkcg->blkg_list.first,
+                                               struct blkio_group, 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);
                }
+       }
 
-               blkg = hlist_entry(blkcg->blkg_list.first, struct blkio_group,
-                                       blkcg_node);
-               key = rcu_dereference(blkg->key);
-               __blkiocg_del_blkio_group(blkg);
-
-               spin_unlock_irqrestore(&blkcg->lock, flags);
-
-               /*
-                * This blkio_group is being unlinked as associated cgroup is
-                * going away. Let all the IO controlling policies know about
-                * this event.
-                */
-               spin_lock(&blkio_list_lock);
-               list_for_each_entry(blkiop, &blkio_list, list) {
-                       if (blkiop->plid != blkg->plid)
-                               continue;
-                       blkiop->ops.blkio_unlink_group_fn(key, blkg);
-               }
-               spin_unlock(&blkio_list_lock);
-       } while (1);
+       spin_unlock_irq(&blkcg->lock);
+       return 0;
+}
 
-       list_for_each_entry_safe(pn, pntmp, &blkcg->policy_list, node) {
-               blkio_policy_delete_node(pn);
-               kfree(pn);
-       }
+static void blkiocg_destroy(struct cgroup *cgroup)
+{
+       struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgroup);
 
-       free_css_id(&blkio_subsys, &blkcg->css);
-       rcu_read_unlock();
        if (blkcg != &blkio_root_cgroup)
                kfree(blkcg);
 }
 
 static struct cgroup_subsys_state *blkiocg_create(struct cgroup *cgroup)
 {
+       static atomic64_t id_seq = ATOMIC64_INIT(0);
        struct blkio_cgroup *blkcg;
        struct cgroup *parent = cgroup->parent;
 
@@ -1609,15 +611,73 @@ static struct cgroup_subsys_state *blkiocg_create(struct cgroup *cgroup)
        if (!blkcg)
                return ERR_PTR(-ENOMEM);
 
-       blkcg->weight = BLKIO_WEIGHT_DEFAULT;
+       blkcg->cfq_weight = CFQ_WEIGHT_DEFAULT;
+       blkcg->id = atomic64_inc_return(&id_seq); /* root is 0, start from 1 */
 done:
        spin_lock_init(&blkcg->lock);
        INIT_HLIST_HEAD(&blkcg->blkg_list);
 
-       INIT_LIST_HEAD(&blkcg->policy_list);
        return &blkcg->css;
 }
 
+/**
+ * blkcg_init_queue - initialize blkcg part of request queue
+ * @q: request_queue to initialize
+ *
+ * Called from blk_alloc_queue_node(). Responsible for initializing blkcg
+ * part of new request_queue @q.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+int blkcg_init_queue(struct request_queue *q)
+{
+       int ret;
+
+       might_sleep();
+
+       ret = blk_throtl_init(q);
+       if (ret)
+               return ret;
+
+       mutex_lock(&all_q_mutex);
+       INIT_LIST_HEAD(&q->all_q_node);
+       list_add_tail(&q->all_q_node, &all_q_list);
+       mutex_unlock(&all_q_mutex);
+
+       return 0;
+}
+
+/**
+ * blkcg_drain_queue - drain blkcg part of request_queue
+ * @q: request_queue to drain
+ *
+ * Called from blk_drain_queue().  Responsible for draining blkcg part.
+ */
+void blkcg_drain_queue(struct request_queue *q)
+{
+       lockdep_assert_held(q->queue_lock);
+
+       blk_throtl_drain(q);
+}
+
+/**
+ * blkcg_exit_queue - exit and release blkcg part of request_queue
+ * @q: request_queue being released
+ *
+ * Called from blk_release_queue().  Responsible for exiting blkcg part.
+ */
+void blkcg_exit_queue(struct request_queue *q)
+{
+       mutex_lock(&all_q_mutex);
+       list_del_init(&q->all_q_node);
+       mutex_unlock(&all_q_mutex);
+
+       blkg_destroy_all(q, true);
+
+       blk_throtl_exit(q);
+}
+
 /*
  * We cannot support shared io contexts, as we have no mean to support
  * two tasks with the same ioc in two different groups without major rework
@@ -1643,47 +703,80 @@ static int blkiocg_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
        return ret;
 }
 
-static void blkiocg_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
+static void blkcg_bypass_start(void)
+       __acquires(&all_q_mutex)
 {
-       struct task_struct *task;
-       struct io_context *ioc;
+       struct request_queue *q;
 
-       cgroup_taskset_for_each(task, cgrp, tset) {
-               /* we don't lose anything even if ioc allocation fails */
-               ioc = get_task_io_context(task, GFP_ATOMIC, NUMA_NO_NODE);
-               if (ioc) {
-                       ioc_cgroup_changed(ioc);
-                       put_io_context(ioc);
-               }
+       mutex_lock(&all_q_mutex);
+
+       list_for_each_entry(q, &all_q_list, all_q_node) {
+               blk_queue_bypass_start(q);
+               blkg_destroy_all(q, false);
        }
 }
 
+static void blkcg_bypass_end(void)
+       __releases(&all_q_mutex)
+{
+       struct request_queue *q;
+
+       list_for_each_entry(q, &all_q_list, all_q_node)
+               blk_queue_bypass_end(q);
+
+       mutex_unlock(&all_q_mutex);
+}
+
+struct cgroup_subsys blkio_subsys = {
+       .name = "blkio",
+       .create = blkiocg_create,
+       .can_attach = blkiocg_can_attach,
+       .pre_destroy = blkiocg_pre_destroy,
+       .destroy = blkiocg_destroy,
+       .subsys_id = blkio_subsys_id,
+       .base_cftypes = blkio_files,
+       .module = THIS_MODULE,
+};
+EXPORT_SYMBOL_GPL(blkio_subsys);
+
 void blkio_policy_register(struct blkio_policy_type *blkiop)
 {
+       struct request_queue *q;
+
+       blkcg_bypass_start();
        spin_lock(&blkio_list_lock);
+
+       BUG_ON(blkio_policy[blkiop->plid]);
+       blkio_policy[blkiop->plid] = blkiop;
        list_add_tail(&blkiop->list, &blkio_list);
+
        spin_unlock(&blkio_list_lock);
+       list_for_each_entry(q, &all_q_list, all_q_node)
+               update_root_blkg_pd(q, blkiop->plid);
+       blkcg_bypass_end();
+
+       if (blkiop->cftypes)
+               WARN_ON(cgroup_add_cftypes(&blkio_subsys, blkiop->cftypes));
 }
 EXPORT_SYMBOL_GPL(blkio_policy_register);
 
 void blkio_policy_unregister(struct blkio_policy_type *blkiop)
 {
+       struct request_queue *q;
+
+       if (blkiop->cftypes)
+               cgroup_rm_cftypes(&blkio_subsys, blkiop->cftypes);
+
+       blkcg_bypass_start();
        spin_lock(&blkio_list_lock);
+
+       BUG_ON(blkio_policy[blkiop->plid] != blkiop);
+       blkio_policy[blkiop->plid] = NULL;
        list_del_init(&blkiop->list);
+
        spin_unlock(&blkio_list_lock);
+       list_for_each_entry(q, &all_q_list, all_q_node)
+               update_root_blkg_pd(q, blkiop->plid);
+       blkcg_bypass_end();
 }
 EXPORT_SYMBOL_GPL(blkio_policy_unregister);
-
-static int __init init_cgroup_blkio(void)
-{
-       return cgroup_load_subsys(&blkio_subsys);
-}
-
-static void __exit exit_cgroup_blkio(void)
-{
-       cgroup_unload_subsys(&blkio_subsys);
-}
-
-module_init(init_cgroup_blkio);
-module_exit(exit_cgroup_blkio);
-MODULE_LICENSE("GPL");