|
@@ -2555,3 +2555,176 @@ static void choose_service_tree(struct cfq_data *cfqd, struct cfq_group *cfqg)
|
|
|
|
|
|
new_workload:
|
|
|
/* otherwise select new workload type */
|
|
|
+ cfqd->serving_type =
|
|
|
+ cfq_choose_wl(cfqd, cfqg, cfqd->serving_prio);
|
|
|
+ st = service_tree_for(cfqg, cfqd->serving_prio, cfqd->serving_type);
|
|
|
+ count = st->count;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * the workload slice is computed as a fraction of target latency
|
|
|
+ * proportional to the number of queues in that workload, over
|
|
|
+ * all the queues in the same priority class
|
|
|
+ */
|
|
|
+ group_slice = cfq_group_slice(cfqd, cfqg);
|
|
|
+
|
|
|
+ slice = group_slice * count /
|
|
|
+ max_t(unsigned, cfqg->busy_queues_avg[cfqd->serving_prio],
|
|
|
+ cfq_group_busy_queues_wl(cfqd->serving_prio, cfqd, cfqg));
|
|
|
+
|
|
|
+ if (cfqd->serving_type == ASYNC_WORKLOAD) {
|
|
|
+ unsigned int tmp;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Async queues are currently system wide. Just taking
|
|
|
+ * proportion of queues with-in same group will lead to higher
|
|
|
+ * async ratio system wide as generally root group is going
|
|
|
+ * to have higher weight. A more accurate thing would be to
|
|
|
+ * calculate system wide asnc/sync ratio.
|
|
|
+ */
|
|
|
+ tmp = cfqd->cfq_target_latency *
|
|
|
+ cfqg_busy_async_queues(cfqd, cfqg);
|
|
|
+ tmp = tmp/cfqd->busy_queues;
|
|
|
+ slice = min_t(unsigned, slice, tmp);
|
|
|
+
|
|
|
+ /* async workload slice is scaled down according to
|
|
|
+ * the sync/async slice ratio. */
|
|
|
+ slice = slice * cfqd->cfq_slice[0] / cfqd->cfq_slice[1];
|
|
|
+ } else
|
|
|
+ /* sync workload slice is at least 2 * cfq_slice_idle */
|
|
|
+ slice = max(slice, 2 * cfqd->cfq_slice_idle);
|
|
|
+
|
|
|
+ slice = max_t(unsigned, slice, CFQ_MIN_TT);
|
|
|
+ cfq_log(cfqd, "workload slice:%d", slice);
|
|
|
+ cfqd->workload_expires = jiffies + slice;
|
|
|
+}
|
|
|
+
|
|
|
+static struct cfq_group *cfq_get_next_cfqg(struct cfq_data *cfqd)
|
|
|
+{
|
|
|
+ struct cfq_rb_root *st = &cfqd->grp_service_tree;
|
|
|
+ struct cfq_group *cfqg;
|
|
|
+
|
|
|
+ if (RB_EMPTY_ROOT(&st->rb))
|
|
|
+ return NULL;
|
|
|
+ cfqg = cfq_rb_first_group(st);
|
|
|
+ update_min_vdisktime(st);
|
|
|
+ return cfqg;
|
|
|
+}
|
|
|
+
|
|
|
+static void cfq_choose_cfqg(struct cfq_data *cfqd)
|
|
|
+{
|
|
|
+ struct cfq_group *cfqg = cfq_get_next_cfqg(cfqd);
|
|
|
+
|
|
|
+ cfqd->serving_group = cfqg;
|
|
|
+
|
|
|
+ /* Restore the workload type data */
|
|
|
+ if (cfqg->saved_workload_slice) {
|
|
|
+ cfqd->workload_expires = jiffies + cfqg->saved_workload_slice;
|
|
|
+ cfqd->serving_type = cfqg->saved_workload;
|
|
|
+ cfqd->serving_prio = cfqg->saved_serving_prio;
|
|
|
+ } else
|
|
|
+ cfqd->workload_expires = jiffies - 1;
|
|
|
+
|
|
|
+ choose_service_tree(cfqd, cfqg);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Select a queue for service. If we have a current active queue,
|
|
|
+ * check whether to continue servicing it, or retrieve and set a new one.
|
|
|
+ */
|
|
|
+static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd)
|
|
|
+{
|
|
|
+ struct cfq_queue *cfqq, *new_cfqq = NULL;
|
|
|
+
|
|
|
+ cfqq = cfqd->active_queue;
|
|
|
+ if (!cfqq)
|
|
|
+ goto new_queue;
|
|
|
+
|
|
|
+ if (!cfqd->rq_queued)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We were waiting for group to get backlogged. Expire the queue
|
|
|
+ */
|
|
|
+ if (cfq_cfqq_wait_busy(cfqq) && !RB_EMPTY_ROOT(&cfqq->sort_list))
|
|
|
+ goto expire;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The active queue has run out of time, expire it and select new.
|
|
|
+ */
|
|
|
+ if (cfq_slice_used(cfqq) && !cfq_cfqq_must_dispatch(cfqq)) {
|
|
|
+ /*
|
|
|
+ * If slice had not expired at the completion of last request
|
|
|
+ * we might not have turned on wait_busy flag. Don't expire
|
|
|
+ * the queue yet. Allow the group to get backlogged.
|
|
|
+ *
|
|
|
+ * The very fact that we have used the slice, that means we
|
|
|
+ * have been idling all along on this queue and it should be
|
|
|
+ * ok to wait for this request to complete.
|
|
|
+ */
|
|
|
+ if (cfqq->cfqg->nr_cfqq == 1 && RB_EMPTY_ROOT(&cfqq->sort_list)
|
|
|
+ && cfqq->dispatched && cfq_should_idle(cfqd, cfqq)) {
|
|
|
+ cfqq = NULL;
|
|
|
+ goto keep_queue;
|
|
|
+ } else
|
|
|
+ goto check_group_idle;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The active queue has requests and isn't expired, allow it to
|
|
|
+ * dispatch.
|
|
|
+ */
|
|
|
+ if (!RB_EMPTY_ROOT(&cfqq->sort_list))
|
|
|
+ goto keep_queue;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If another queue has a request waiting within our mean seek
|
|
|
+ * distance, let it run. The expire code will check for close
|
|
|
+ * cooperators and put the close queue at the front of the service
|
|
|
+ * tree. If possible, merge the expiring queue with the new cfqq.
|
|
|
+ */
|
|
|
+ new_cfqq = cfq_close_cooperator(cfqd, cfqq);
|
|
|
+ if (new_cfqq) {
|
|
|
+ if (!cfqq->new_cfqq)
|
|
|
+ cfq_setup_merge(cfqq, new_cfqq);
|
|
|
+ goto expire;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * No requests pending. If the active queue still has requests in
|
|
|
+ * flight or is idling for a new request, allow either of these
|
|
|
+ * conditions to happen (or time out) before selecting a new queue.
|
|
|
+ */
|
|
|
+ if (timer_pending(&cfqd->idle_slice_timer)) {
|
|
|
+ cfqq = NULL;
|
|
|
+ goto keep_queue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * This is a deep seek queue, but the device is much faster than
|
|
|
+ * the queue can deliver, don't idle
|
|
|
+ **/
|
|
|
+ if (CFQQ_SEEKY(cfqq) && cfq_cfqq_idle_window(cfqq) &&
|
|
|
+ (cfq_cfqq_slice_new(cfqq) ||
|
|
|
+ (cfqq->slice_end - jiffies > jiffies - cfqq->slice_start))) {
|
|
|
+ cfq_clear_cfqq_deep(cfqq);
|
|
|
+ cfq_clear_cfqq_idle_window(cfqq);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cfqq->dispatched && cfq_should_idle(cfqd, cfqq)) {
|
|
|
+ cfqq = NULL;
|
|
|
+ goto keep_queue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If group idle is enabled and there are requests dispatched from
|
|
|
+ * this group, wait for requests to complete.
|
|
|
+ */
|
|
|
+check_group_idle:
|
|
|
+ if (cfqd->cfq_group_idle && cfqq->cfqg->nr_cfqq == 1 &&
|
|
|
+ cfqq->cfqg->dispatched &&
|
|
|
+ !cfq_io_thinktime_big(cfqd, &cfqq->cfqg->ttime, true)) {
|
|
|
+ cfqq = NULL;
|
|
|
+ goto keep_queue;
|
|
|
+ }
|
|
|
+
|
|
|
+expire:
|