]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - block/blk-throttle.c
mtd: create_freezable_workqueue() doesn't return an ERR_PTR
[karo-tx-linux.git] / block / blk-throttle.c
index 541bd0dabb9a92f3e795bf4cd011e2cbc4b955d2..08a32dfd3844cfeeacae4d38164c96f05f1b0c83 100644 (file)
@@ -124,6 +124,9 @@ struct throtl_grp {
 
        unsigned int flags;
 
+       /* are there any throtl rules between this group and td? */
+       bool has_rules[2];
+
        /* bytes per second rate limits */
        uint64_t bps[2];
 
@@ -394,10 +397,30 @@ static void throtl_pd_init(struct blkcg_gq *blkg)
 {
        struct throtl_grp *tg = blkg_to_tg(blkg);
        struct throtl_data *td = blkg->q->td;
+       struct throtl_service_queue *parent_sq;
        unsigned long flags;
        int rw;
 
-       throtl_service_queue_init(&tg->service_queue, &td->service_queue);
+       /*
+        * If sane_hierarchy is enabled, we switch to properly hierarchical
+        * behavior where limits on a given throtl_grp are applied to the
+        * whole subtree rather than just the group itself.  e.g. If 16M
+        * read_bps limit is set on the root group, the whole system can't
+        * exceed 16M for the device.
+        *
+        * If sane_hierarchy is not enabled, the broken flat hierarchy
+        * behavior is retained where all throtl_grps are treated as if
+        * they're all separate root groups right below throtl_data.
+        * Limits of a group don't interact with limits of other groups
+        * regardless of the position of the group in the hierarchy.
+        */
+       parent_sq = &td->service_queue;
+
+       if (cgroup_sane_behavior(blkg->blkcg->css.cgroup) && blkg->parent)
+               parent_sq = &blkg_to_tg(blkg->parent)->service_queue;
+
+       throtl_service_queue_init(&tg->service_queue, parent_sq);
+
        for (rw = READ; rw <= WRITE; rw++) {
                throtl_qnode_init(&tg->qnode_on_self[rw], tg);
                throtl_qnode_init(&tg->qnode_on_parent[rw], tg);
@@ -422,6 +445,30 @@ static void throtl_pd_init(struct blkcg_gq *blkg)
        spin_unlock_irqrestore(&tg_stats_alloc_lock, flags);
 }
 
+/*
+ * Set has_rules[] if @tg or any of its parents have limits configured.
+ * This doesn't require walking up to the top of the hierarchy as the
+ * parent's has_rules[] is guaranteed to be correct.
+ */
+static void tg_update_has_rules(struct throtl_grp *tg)
+{
+       struct throtl_grp *parent_tg = sq_to_tg(tg->service_queue.parent_sq);
+       int rw;
+
+       for (rw = READ; rw <= WRITE; rw++)
+               tg->has_rules[rw] = (parent_tg && parent_tg->has_rules[rw]) ||
+                                   (tg->bps[rw] != -1 || tg->iops[rw] != -1);
+}
+
+static void throtl_pd_online(struct blkcg_gq *blkg)
+{
+       /*
+        * We don't want new groups to escape the limits of its ancestors.
+        * Update has_rules[] after a new group is brought online.
+        */
+       tg_update_has_rules(blkg_to_tg(blkg));
+}
+
 static void throtl_pd_exit(struct blkcg_gq *blkg)
 {
        struct throtl_grp *tg = blkg_to_tg(blkg);
@@ -633,6 +680,28 @@ static bool throtl_schedule_next_dispatch(struct throtl_service_queue *sq,
        return false;
 }
 
+static inline void throtl_start_new_slice_with_credit(struct throtl_grp *tg,
+               bool rw, unsigned long start)
+{
+       tg->bytes_disp[rw] = 0;
+       tg->io_disp[rw] = 0;
+
+       /*
+        * Previous slice has expired. We must have trimmed it after last
+        * bio dispatch. That means since start of last slice, we never used
+        * that bandwidth. Do try to make use of that bandwidth while giving
+        * credit.
+        */
+       if (time_after_eq(start, tg->slice_start[rw]))
+               tg->slice_start[rw] = start;
+
+       tg->slice_end[rw] = jiffies + throtl_slice;
+       throtl_log(&tg->service_queue,
+                  "[%c] new slice with credit start=%lu end=%lu jiffies=%lu",
+                  rw == READ ? 'R' : 'W', tg->slice_start[rw],
+                  tg->slice_end[rw], jiffies);
+}
+
 static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw)
 {
        tg->bytes_disp[rw] = 0;
@@ -821,12 +890,6 @@ static bool tg_with_in_bps_limit(struct throtl_grp *tg, struct bio *bio,
        return 0;
 }
 
-static bool tg_no_rule_group(struct throtl_grp *tg, bool rw) {
-       if (tg->bps[rw] == -1 && tg->iops[rw] == -1)
-               return 1;
-       return 0;
-}
-
 /*
  * Returns whether one can dispatch a bio or not. Also returns approx number
  * of jiffies to wait before this bio is with-in IO rate and can be dispatched
@@ -992,6 +1055,16 @@ static void tg_update_disptime(struct throtl_grp *tg)
        tg->flags &= ~THROTL_TG_WAS_EMPTY;
 }
 
+static void start_parent_slice_with_credit(struct throtl_grp *child_tg,
+                                       struct throtl_grp *parent_tg, bool rw)
+{
+       if (throtl_slice_used(parent_tg, rw)) {
+               throtl_start_new_slice_with_credit(parent_tg, rw,
+                               child_tg->slice_start[rw]);
+       }
+
+}
+
 static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw)
 {
        struct throtl_service_queue *sq = &tg->service_queue;
@@ -1020,6 +1093,7 @@ static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw)
         */
        if (parent_tg) {
                throtl_add_bio_tg(bio, &tg->qnode_on_parent[rw], parent_tg);
+               start_parent_slice_with_credit(tg, parent_tg, rw);
        } else {
                throtl_qnode_add_bio(bio, &tg->qnode_on_parent[rw],
                                     &parent_sq->queued[rw]);
@@ -1274,6 +1348,8 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf,
        struct blkg_conf_ctx ctx;
        struct throtl_grp *tg;
        struct throtl_service_queue *sq;
+       struct blkcg_gq *blkg;
+       struct cgroup *pos_cgrp;
        int ret;
 
        ret = blkg_conf_prep(blkcg, &blkcg_policy_throtl, buf, &ctx);
@@ -1296,6 +1372,17 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf,
                   tg->bps[READ], tg->bps[WRITE],
                   tg->iops[READ], tg->iops[WRITE]);
 
+       /*
+        * Update has_rules[] flags for the updated tg's subtree.  A tg is
+        * considered to have rules if either the tg itself or any of its
+        * ancestors has rules.  This identifies groups without any
+        * restrictions in the whole hierarchy and allows them to bypass
+        * blk-throttle.
+        */
+       tg_update_has_rules(tg);
+       blkg_for_each_descendant_pre(blkg, pos_cgrp, ctx.blkg)
+               tg_update_has_rules(blkg_to_tg(blkg));
+
        /*
         * We're already holding queue_lock and know @tg is valid.  Let's
         * apply the new config directly.
@@ -1382,6 +1469,7 @@ static struct blkcg_policy blkcg_policy_throtl = {
        .cftypes                = throtl_files,
 
        .pd_init_fn             = throtl_pd_init,
+       .pd_online_fn           = throtl_pd_online,
        .pd_exit_fn             = throtl_pd_exit,
        .pd_reset_stats_fn      = throtl_pd_reset_stats,
 };
@@ -1409,7 +1497,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
        blkcg = bio_blkcg(bio);
        tg = throtl_lookup_tg(td, blkcg);
        if (tg) {
-               if (tg_no_rule_group(tg, rw)) {
+               if (!tg->has_rules[rw]) {
                        throtl_update_dispatch_stats(tg_to_blkg(tg),
                                                     bio->bi_size, bio->bi_rw);
                        goto out_unlock_rcu;