block, bfq: do not expire a queue that will deserve dispatch plugging
[linux-2.6-microblaze.git] / block / bfq-iosched.c
index 495b9dd..4fd4f19 100644 (file)
@@ -742,8 +742,9 @@ inc_counter:
  * See the comments to the function bfq_weights_tree_add() for considerations
  * about overhead.
  */
-void bfq_weights_tree_remove(struct bfq_data *bfqd, struct bfq_entity *entity,
-                            struct rb_root *root)
+void __bfq_weights_tree_remove(struct bfq_data *bfqd,
+                              struct bfq_entity *entity,
+                              struct rb_root *root)
 {
        if (!entity->weight_counter)
                return;
@@ -759,6 +760,43 @@ reset_entity_pointer:
        entity->weight_counter = NULL;
 }
 
+/*
+ * Invoke __bfq_weights_tree_remove on bfqq and all its inactive
+ * parent entities.
+ */
+void bfq_weights_tree_remove(struct bfq_data *bfqd,
+                            struct bfq_queue *bfqq)
+{
+       struct bfq_entity *entity = bfqq->entity.parent;
+
+       __bfq_weights_tree_remove(bfqd, &bfqq->entity,
+                                 &bfqd->queue_weights_tree);
+
+       for_each_entity(entity) {
+               struct bfq_sched_data *sd = entity->my_sched_data;
+
+               if (sd->next_in_service || sd->in_service_entity) {
+                       /*
+                        * entity is still active, because either
+                        * next_in_service or in_service_entity is not
+                        * NULL (see the comments on the definition of
+                        * next_in_service for details on why
+                        * in_service_entity must be checked too).
+                        *
+                        * As a consequence, the weight of entity is
+                        * not to be removed. In addition, if entity
+                        * is active, then its parent entities are
+                        * active as well, and thus their weights are
+                        * not to be removed either. In the end, this
+                        * loop must stop here.
+                        */
+                       break;
+               }
+               __bfq_weights_tree_remove(bfqd, entity,
+                                         &bfqd->group_weights_tree);
+       }
+}
+
 /*
  * Return expired entry, or NULL to just start from scratch in rbtree.
  */
@@ -3559,8 +3597,14 @@ static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd)
 
        bfq_log_bfqq(bfqd, bfqq, "select_queue: already in-service queue");
 
+       /*
+        * Do not expire bfqq for budget timeout if bfqq may be about
+        * to enjoy device idling. The reason why, in this case, we
+        * prevent bfqq from expiring is the same as in the comments
+        * on the case where bfq_bfqq_must_idle() returns true, in
+        * bfq_completed_request().
+        */
        if (bfq_may_expire_for_budg_timeout(bfqq) &&
-           !bfq_bfqq_wait_request(bfqq) &&
            !bfq_bfqq_must_idle(bfqq))
                goto expire;
 
@@ -4582,8 +4626,7 @@ static void bfq_completed_request(struct bfq_queue *bfqq, struct bfq_data *bfqd)
                 */
                bfqq->budget_timeout = jiffies;
 
-               bfq_weights_tree_remove(bfqd, &bfqq->entity,
-                                       &bfqd->queue_weights_tree);
+               bfq_weights_tree_remove(bfqd, bfqq);
        }
 
        now_ns = ktime_get_ns();
@@ -4637,8 +4680,32 @@ static void bfq_completed_request(struct bfq_queue *bfqq, struct bfq_data *bfqd)
         * or if we want to idle in case it has no pending requests.
         */
        if (bfqd->in_service_queue == bfqq) {
-               if (bfqq->dispatched == 0 && bfq_bfqq_must_idle(bfqq)) {
-                       bfq_arm_slice_timer(bfqd);
+               if (bfq_bfqq_must_idle(bfqq)) {
+                       if (bfqq->dispatched == 0)
+                               bfq_arm_slice_timer(bfqd);
+                       /*
+                        * If we get here, we do not expire bfqq, even
+                        * if bfqq was in budget timeout or had no
+                        * more requests (as controlled in the next
+                        * conditional instructions). The reason for
+                        * not expiring bfqq is as follows.
+                        *
+                        * Here bfqq->dispatched > 0 holds, but
+                        * bfq_bfqq_must_idle() returned true. This
+                        * implies that, even if no request arrives
+                        * for bfqq before bfqq->dispatched reaches 0,
+                        * bfqq will, however, not be expired on the
+                        * completion event that causes bfqq->dispatch
+                        * to reach zero. In contrast, on this event,
+                        * bfqq will start enjoying device idling
+                        * (I/O-dispatch plugging).
+                        *
+                        * But, if we expired bfqq here, bfqq would
+                        * not have the chance to enjoy device idling
+                        * when bfqq->dispatched finally reaches
+                        * zero. This would expose bfqq to violation
+                        * of its reserved service guarantees.
+                        */
                        return;
                } else if (bfq_may_expire_for_budg_timeout(bfqq))
                        bfq_bfqq_expire(bfqd, bfqq, false,