Merge tag 'hardening-v6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees...
[platform/kernel/linux-rpi.git] / block / blk-cgroup.c
index bd50b55..ff45649 100644 (file)
@@ -33,7 +33,6 @@
 #include "blk-cgroup.h"
 #include "blk-ioprio.h"
 #include "blk-throttle.h"
-#include "blk-rq-qos.h"
 
 /*
  * blkcg_pol_mutex protects blkcg_policy[] and policy [de]activation.
@@ -56,7 +55,6 @@ static struct blkcg_policy *blkcg_policy[BLKCG_MAX_POLS];
 static LIST_HEAD(all_blkcgs);          /* protected by blkcg_pol_mutex */
 
 bool blkcg_debug_stats = false;
-static struct workqueue_struct *blkcg_punt_bio_wq;
 
 #define BLKG_DESTROY_BATCH_SIZE  64
 
@@ -166,7 +164,9 @@ static void __blkg_release(struct rcu_head *rcu)
 {
        struct blkcg_gq *blkg = container_of(rcu, struct blkcg_gq, rcu_head);
 
+#ifdef CONFIG_BLK_CGROUP_PUNT_BIO
        WARN_ON(!bio_list_empty(&blkg->async_bios));
+#endif
 
        /* release the blkcg and parent blkg refs this blkg has been holding */
        css_put(&blkg->blkcg->css);
@@ -188,6 +188,9 @@ static void blkg_release(struct percpu_ref *ref)
        call_rcu(&blkg->rcu_head, __blkg_release);
 }
 
+#ifdef CONFIG_BLK_CGROUP_PUNT_BIO
+static struct workqueue_struct *blkcg_punt_bio_wq;
+
 static void blkg_async_bio_workfn(struct work_struct *work)
 {
        struct blkcg_gq *blkg = container_of(work, struct blkcg_gq,
@@ -198,10 +201,10 @@ static void blkg_async_bio_workfn(struct work_struct *work)
        bool need_plug = false;
 
        /* as long as there are pending bios, @blkg can't go away */
-       spin_lock_bh(&blkg->async_bio_lock);
+       spin_lock(&blkg->async_bio_lock);
        bio_list_merge(&bios, &blkg->async_bios);
        bio_list_init(&blkg->async_bios);
-       spin_unlock_bh(&blkg->async_bio_lock);
+       spin_unlock(&blkg->async_bio_lock);
 
        /* start plug only when bio_list contains at least 2 bios */
        if (bios.head && bios.head->bi_next) {
@@ -214,6 +217,40 @@ static void blkg_async_bio_workfn(struct work_struct *work)
                blk_finish_plug(&plug);
 }
 
+/*
+ * When a shared kthread issues a bio for a cgroup, doing so synchronously can
+ * lead to priority inversions as the kthread can be trapped waiting for that
+ * cgroup.  Use this helper instead of submit_bio to punt the actual issuing to
+ * a dedicated per-blkcg work item to avoid such priority inversions.
+ */
+void blkcg_punt_bio_submit(struct bio *bio)
+{
+       struct blkcg_gq *blkg = bio->bi_blkg;
+
+       if (blkg->parent) {
+               spin_lock(&blkg->async_bio_lock);
+               bio_list_add(&blkg->async_bios, bio);
+               spin_unlock(&blkg->async_bio_lock);
+               queue_work(blkcg_punt_bio_wq, &blkg->async_bio_work);
+       } else {
+               /* never bounce for the root cgroup */
+               submit_bio(bio);
+       }
+}
+EXPORT_SYMBOL_GPL(blkcg_punt_bio_submit);
+
+static int __init blkcg_punt_bio_init(void)
+{
+       blkcg_punt_bio_wq = alloc_workqueue("blkcg_punt_bio",
+                                           WQ_MEM_RECLAIM | WQ_FREEZABLE |
+                                           WQ_UNBOUND | WQ_SYSFS, 0);
+       if (!blkcg_punt_bio_wq)
+               return -ENOMEM;
+       return 0;
+}
+subsys_initcall(blkcg_punt_bio_init);
+#endif /* CONFIG_BLK_CGROUP_PUNT_BIO */
+
 /**
  * bio_blkcg_css - return the blkcg CSS associated with a bio
  * @bio: target bio
@@ -269,10 +306,12 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct gendisk *disk,
 
        blkg->q = disk->queue;
        INIT_LIST_HEAD(&blkg->q_node);
+       blkg->blkcg = blkcg;
+#ifdef CONFIG_BLK_CGROUP_PUNT_BIO
        spin_lock_init(&blkg->async_bio_lock);
        bio_list_init(&blkg->async_bios);
        INIT_WORK(&blkg->async_bio_work, blkg_async_bio_workfn);
-       blkg->blkcg = blkcg;
+#endif
 
        u64_stats_init(&blkg->iostat.sync);
        for_each_possible_cpu(cpu) {
@@ -653,69 +692,93 @@ u64 __blkg_prfill_u64(struct seq_file *sf, struct blkg_policy_data *pd, u64 v)
 EXPORT_SYMBOL_GPL(__blkg_prfill_u64);
 
 /**
- * blkcg_conf_open_bdev - parse and open bdev for per-blkg config update
- * @inputp: input string pointer
+ * blkg_conf_init - initialize a blkg_conf_ctx
+ * @ctx: blkg_conf_ctx to initialize
+ * @input: input string
+ *
+ * Initialize @ctx which can be used to parse blkg config input string @input.
+ * Once initialized, @ctx can be used with blkg_conf_open_bdev() and
+ * blkg_conf_prep(), and must be cleaned up with blkg_conf_exit().
+ */
+void blkg_conf_init(struct blkg_conf_ctx *ctx, char *input)
+{
+       *ctx = (struct blkg_conf_ctx){ .input = input };
+}
+EXPORT_SYMBOL_GPL(blkg_conf_init);
+
+/**
+ * blkg_conf_open_bdev - parse and open bdev for per-blkg config update
+ * @ctx: blkg_conf_ctx initialized with blkg_conf_init()
  *
- * Parse the device node prefix part, MAJ:MIN, of per-blkg config update
- * from @input and get and return the matching bdev.  *@inputp is
- * updated to point past the device node prefix.  Returns an ERR_PTR()
- * value on error.
+ * Parse the device node prefix part, MAJ:MIN, of per-blkg config update from
+ * @ctx->input and get and store the matching bdev in @ctx->bdev. @ctx->body is
+ * set to point past the device node prefix.
  *
- * Use this function iff blkg_conf_prep() can't be used for some reason.
+ * This function may be called multiple times on @ctx and the extra calls become
+ * NOOPs. blkg_conf_prep() implicitly calls this function. Use this function
+ * explicitly if bdev access is needed without resolving the blkcg / policy part
+ * of @ctx->input. Returns -errno on error.
  */
-struct block_device *blkcg_conf_open_bdev(char **inputp)
+int blkg_conf_open_bdev(struct blkg_conf_ctx *ctx)
 {
-       char *input = *inputp;
+       char *input = ctx->input;
        unsigned int major, minor;
        struct block_device *bdev;
        int key_len;
 
+       if (ctx->bdev)
+               return 0;
+
        if (sscanf(input, "%u:%u%n", &major, &minor, &key_len) != 2)
-               return ERR_PTR(-EINVAL);
+               return -EINVAL;
 
        input += key_len;
        if (!isspace(*input))
-               return ERR_PTR(-EINVAL);
+               return -EINVAL;
        input = skip_spaces(input);
 
        bdev = blkdev_get_no_open(MKDEV(major, minor));
        if (!bdev)
-               return ERR_PTR(-ENODEV);
+               return -ENODEV;
        if (bdev_is_partition(bdev)) {
                blkdev_put_no_open(bdev);
-               return ERR_PTR(-ENODEV);
+               return -ENODEV;
        }
 
-       *inputp = input;
-       return bdev;
+       ctx->body = input;
+       ctx->bdev = bdev;
+       return 0;
 }
 
 /**
  * blkg_conf_prep - parse and prepare for per-blkg config update
  * @blkcg: target block cgroup
  * @pol: target policy
- * @input: input string
- * @ctx: blkg_conf_ctx to be filled
+ * @ctx: blkg_conf_ctx initialized with blkg_conf_init()
+ *
+ * Parse per-blkg config update from @ctx->input and initialize @ctx
+ * accordingly. On success, @ctx->body points to the part of @ctx->input
+ * following MAJ:MIN, @ctx->bdev points to the target block device and
+ * @ctx->blkg to the blkg being configured.
  *
- * Parse per-blkg config update from @input and initialize @ctx with the
- * result.  @ctx->blkg points to the blkg to be updated and @ctx->body the
- * part of @input following MAJ:MIN.  This function returns with RCU read
- * lock and queue lock held and must be paired with blkg_conf_finish().
+ * blkg_conf_open_bdev() may be called on @ctx beforehand. On success, this
+ * function returns with queue lock held and must be followed by
+ * blkg_conf_exit().
  */
 int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
-                  char *input, struct blkg_conf_ctx *ctx)
-       __acquires(rcu) __acquires(&bdev->bd_queue->queue_lock)
+                  struct blkg_conf_ctx *ctx)
+       __acquires(&bdev->bd_queue->queue_lock)
 {
-       struct block_device *bdev;
        struct gendisk *disk;
        struct request_queue *q;
        struct blkcg_gq *blkg;
        int ret;
 
-       bdev = blkcg_conf_open_bdev(&input);
-       if (IS_ERR(bdev))
-               return PTR_ERR(bdev);
-       disk = bdev->bd_disk;
+       ret = blkg_conf_open_bdev(ctx);
+       if (ret)
+               return ret;
+
+       disk = ctx->bdev->bd_disk;
        q = disk->queue;
 
        /*
@@ -726,7 +789,6 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
        if (ret)
                goto fail;
 
-       rcu_read_lock();
        spin_lock_irq(&q->queue_lock);
 
        if (!blkcg_policy_enabled(q, pol)) {
@@ -755,7 +817,6 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
 
                /* Drop locks to do new blkg allocation with GFP_KERNEL. */
                spin_unlock_irq(&q->queue_lock);
-               rcu_read_unlock();
 
                new_blkg = blkg_alloc(pos, disk, GFP_KERNEL);
                if (unlikely(!new_blkg)) {
@@ -769,7 +830,6 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
                        goto fail_exit_queue;
                }
 
-               rcu_read_lock();
                spin_lock_irq(&q->queue_lock);
 
                if (!blkcg_policy_enabled(q, pol)) {
@@ -796,20 +856,16 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
        }
 success:
        blk_queue_exit(q);
-       ctx->bdev = bdev;
        ctx->blkg = blkg;
-       ctx->body = input;
        return 0;
 
 fail_preloaded:
        radix_tree_preload_end();
 fail_unlock:
        spin_unlock_irq(&q->queue_lock);
-       rcu_read_unlock();
 fail_exit_queue:
        blk_queue_exit(q);
 fail:
-       blkdev_put_no_open(bdev);
        /*
         * If queue was bypassing, we should retry.  Do so after a
         * short msleep().  It isn't strictly necessary but queue
@@ -825,20 +881,27 @@ fail:
 EXPORT_SYMBOL_GPL(blkg_conf_prep);
 
 /**
- * blkg_conf_finish - finish up per-blkg config update
- * @ctx: blkg_conf_ctx initialized by blkg_conf_prep()
+ * blkg_conf_exit - clean up per-blkg config update
+ * @ctx: blkg_conf_ctx initialized with blkg_conf_init()
  *
- * Finish up after per-blkg config update.  This function must be paired
- * with blkg_conf_prep().
+ * Clean up after per-blkg config update. This function must be called on all
+ * blkg_conf_ctx's initialized with blkg_conf_init().
  */
-void blkg_conf_finish(struct blkg_conf_ctx *ctx)
-       __releases(&ctx->bdev->bd_queue->queue_lock) __releases(rcu)
+void blkg_conf_exit(struct blkg_conf_ctx *ctx)
+       __releases(&ctx->bdev->bd_queue->queue_lock)
 {
-       spin_unlock_irq(&bdev_get_queue(ctx->bdev)->queue_lock);
-       rcu_read_unlock();
-       blkdev_put_no_open(ctx->bdev);
+       if (ctx->blkg) {
+               spin_unlock_irq(&bdev_get_queue(ctx->bdev)->queue_lock);
+               ctx->blkg = NULL;
+       }
+
+       if (ctx->bdev) {
+               blkdev_put_no_open(ctx->bdev);
+               ctx->body = NULL;
+               ctx->bdev = NULL;
+       }
 }
-EXPORT_SYMBOL_GPL(blkg_conf_finish);
+EXPORT_SYMBOL_GPL(blkg_conf_exit);
 
 static void blkg_iostat_set(struct blkg_iostat *dst, struct blkg_iostat *src)
 {
@@ -1249,8 +1312,6 @@ blkcg_css_alloc(struct cgroup_subsys_state *parent_css)
                blkcg->cpd[i] = cpd;
                cpd->blkcg = blkcg;
                cpd->plid = i;
-               if (pol->cpd_init_fn)
-                       pol->cpd_init_fn(cpd);
        }
 
        spin_lock_init(&blkcg->lock);
@@ -1328,14 +1389,8 @@ int blkcg_init_disk(struct gendisk *disk)
        if (ret)
                goto err_ioprio_exit;
 
-       ret = blk_iolatency_init(disk);
-       if (ret)
-               goto err_throtl_exit;
-
        return 0;
 
-err_throtl_exit:
-       blk_throtl_exit(disk);
 err_ioprio_exit:
        blk_ioprio_exit(disk);
 err_destroy_all:
@@ -1351,30 +1406,9 @@ err_unlock:
 void blkcg_exit_disk(struct gendisk *disk)
 {
        blkg_destroy_all(disk);
-       rq_qos_exit(disk->queue);
        blk_throtl_exit(disk);
 }
 
-static void blkcg_bind(struct cgroup_subsys_state *root_css)
-{
-       int i;
-
-       mutex_lock(&blkcg_pol_mutex);
-
-       for (i = 0; i < BLKCG_MAX_POLS; i++) {
-               struct blkcg_policy *pol = blkcg_policy[i];
-               struct blkcg *blkcg;
-
-               if (!pol || !pol->cpd_bind_fn)
-                       continue;
-
-               list_for_each_entry(blkcg, &all_blkcgs, all_blkcgs_node)
-                       if (blkcg->cpd[pol->plid])
-                               pol->cpd_bind_fn(blkcg->cpd[pol->plid]);
-       }
-       mutex_unlock(&blkcg_pol_mutex);
-}
-
 static void blkcg_exit(struct task_struct *tsk)
 {
        if (tsk->throttle_disk)
@@ -1388,7 +1422,6 @@ struct cgroup_subsys io_cgrp_subsys = {
        .css_offline = blkcg_css_offline,
        .css_free = blkcg_css_free,
        .css_rstat_flush = blkcg_rstat_flush,
-       .bind = blkcg_bind,
        .dfl_cftypes = blkcg_files,
        .legacy_cftypes = blkcg_legacy_files,
        .legacy_name = "blkio",
@@ -1626,8 +1659,6 @@ int blkcg_policy_register(struct blkcg_policy *pol)
                        blkcg->cpd[pol->plid] = cpd;
                        cpd->blkcg = blkcg;
                        cpd->plid = pol->plid;
-                       if (pol->cpd_init_fn)
-                               pol->cpd_init_fn(cpd);
                }
        }
 
@@ -1688,25 +1719,6 @@ out_unlock:
 }
 EXPORT_SYMBOL_GPL(blkcg_policy_unregister);
 
-bool __blkcg_punt_bio_submit(struct bio *bio)
-{
-       struct blkcg_gq *blkg = bio->bi_blkg;
-
-       /* consume the flag first */
-       bio->bi_opf &= ~REQ_CGROUP_PUNT;
-
-       /* never bounce for the root cgroup */
-       if (!blkg->parent)
-               return false;
-
-       spin_lock_bh(&blkg->async_bio_lock);
-       bio_list_add(&blkg->async_bios, bio);
-       spin_unlock_bh(&blkg->async_bio_lock);
-
-       queue_work(blkcg_punt_bio_wq, &blkg->async_bio_work);
-       return true;
-}
-
 /*
  * Scale the accumulated delay based on how long it has been since we updated
  * the delay.  We only call this when we are adding delay, in case it's been a
@@ -2085,16 +2097,5 @@ bool blk_cgroup_congested(void)
        return ret;
 }
 
-static int __init blkcg_init(void)
-{
-       blkcg_punt_bio_wq = alloc_workqueue("blkcg_punt_bio",
-                                           WQ_MEM_RECLAIM | WQ_FREEZABLE |
-                                           WQ_UNBOUND | WQ_SYSFS, 0);
-       if (!blkcg_punt_bio_wq)
-               return -ENOMEM;
-       return 0;
-}
-subsys_initcall(blkcg_init);
-
 module_param(blkcg_debug_stats, bool, 0644);
 MODULE_PARM_DESC(blkcg_debug_stats, "True if you want debug stats, false if not");