cfq-iosched: rethink seeky detection for SSDs
[pandora-kernel.git] / block / cfq-iosched.c
index ee130f1..423aee3 100644 (file)
@@ -42,16 +42,14 @@ static const int cfq_hist_divisor = 4;
  */
 #define CFQ_MIN_TT             (2)
 
-/*
- * Allow merged cfqqs to perform this amount of seeky I/O before
- * deciding to break the queues up again.
- */
-#define CFQQ_COOP_TOUT         (HZ)
-
 #define CFQ_SLICE_SCALE                (5)
 #define CFQ_HW_QUEUE_MIN       (5)
 #define CFQ_SERVICE_SHIFT       12
 
+#define CFQQ_SEEK_THR          (sector_t)(8 * 100)
+#define CFQQ_SECT_THR_NONROT   (sector_t)(2 * 32)
+#define CFQQ_SEEKY(cfqq)       (hweight32(cfqq->seek_history) > 32/8)
+
 #define RQ_CIC(rq)             \
        ((struct cfq_io_context *) (rq)->elevator_private)
 #define RQ_CFQQ(rq)            (struct cfq_queue *) ((rq)->elevator_private2)
@@ -118,11 +116,11 @@ struct cfq_queue {
        /* time when queue got scheduled in to dispatch first request. */
        unsigned long dispatch_start;
        unsigned int allocated_slice;
+       unsigned int slice_dispatch;
        /* time when first request from queue completed and slice started. */
        unsigned long slice_start;
        unsigned long slice_end;
        long slice_resid;
-       unsigned int slice_dispatch;
 
        /* pending metadata requests */
        int meta_pending;
@@ -133,14 +131,11 @@ struct cfq_queue {
        unsigned short ioprio, org_ioprio;
        unsigned short ioprio_class, org_ioprio_class;
 
-       unsigned int seek_samples;
-       u64 seek_total;
-       sector_t seek_mean;
-       sector_t last_request_pos;
-       unsigned long seeky_start;
-
        pid_t pid;
 
+       u32 seek_history;
+       sector_t last_request_pos;
+
        struct cfq_rb_root *service_tree;
        struct cfq_queue *new_cfqq;
        struct cfq_group *cfqg;
@@ -314,6 +309,7 @@ enum cfqq_state_flags {
        CFQ_CFQQ_FLAG_slice_new,        /* no requests dispatched in slice */
        CFQ_CFQQ_FLAG_sync,             /* synchronous queue */
        CFQ_CFQQ_FLAG_coop,             /* cfqq is shared */
+       CFQ_CFQQ_FLAG_split_coop,       /* shared cfqq will be splitted */
        CFQ_CFQQ_FLAG_deep,             /* sync cfqq experienced large depth */
        CFQ_CFQQ_FLAG_wait_busy,        /* Waiting for next request */
 };
@@ -342,6 +338,7 @@ CFQ_CFQQ_FNS(prio_changed);
 CFQ_CFQQ_FNS(slice_new);
 CFQ_CFQQ_FNS(sync);
 CFQ_CFQQ_FNS(coop);
+CFQ_CFQQ_FNS(split_coop);
 CFQ_CFQQ_FNS(deep);
 CFQ_CFQQ_FNS(wait_busy);
 #undef CFQ_CFQQ_FNS
@@ -953,10 +950,6 @@ cfq_find_alloc_cfqg(struct cfq_data *cfqd, struct cgroup *cgroup, int create)
        struct backing_dev_info *bdi = &cfqd->queue->backing_dev_info;
        unsigned int major, minor;
 
-       /* Do we need to take this reference */
-       if (!blkiocg_css_tryget(blkcg))
-               return NULL;;
-
        cfqg = cfqg_of_blkg(blkiocg_lookup_group(blkcg, key));
        if (cfqg || !create)
                goto done;
@@ -987,7 +980,6 @@ cfq_find_alloc_cfqg(struct cfq_data *cfqd, struct cgroup *cgroup, int create)
        hlist_add_head(&cfqg->cfqd_node, &cfqd->cfqg_list);
 
 done:
-       blkiocg_css_put(blkcg);
        return cfqg;
 }
 
@@ -1565,6 +1557,15 @@ __cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq,
        cfq_clear_cfqq_wait_request(cfqq);
        cfq_clear_cfqq_wait_busy(cfqq);
 
+       /*
+        * If this cfqq is shared between multiple processes, check to
+        * make sure that those processes are still issuing I/Os within
+        * the mean seek distance.  If not, it may be time to break the
+        * queues apart again.
+        */
+       if (cfq_cfqq_coop(cfqq) && CFQQ_SEEKY(cfqq))
+               cfq_mark_cfqq_split_coop(cfqq);
+
        /*
         * store what was left of this slice, if the queue idled/timed out
         */
@@ -1663,22 +1664,10 @@ static inline sector_t cfq_dist_from_last(struct cfq_data *cfqd,
                return cfqd->last_position - blk_rq_pos(rq);
 }
 
-#define CFQQ_SEEK_THR          8 * 1024
-#define CFQQ_SEEKY(cfqq)       ((cfqq)->seek_mean > CFQQ_SEEK_THR)
-
 static inline int cfq_rq_close(struct cfq_data *cfqd, struct cfq_queue *cfqq,
                               struct request *rq, bool for_preempt)
 {
-       sector_t sdist = cfqq->seek_mean;
-
-       if (!sample_valid(cfqq->seek_samples))
-               sdist = CFQQ_SEEK_THR;
-
-       /* if seek_mean is big, using it as close criteria is meaningless */
-       if (sdist > CFQQ_SEEK_THR && !for_preempt)
-               sdist = CFQQ_SEEK_THR;
-
-       return cfq_dist_from_last(cfqd, rq) <= sdist;
+       return cfq_dist_from_last(cfqd, rq) <= CFQQ_SEEK_THR;
 }
 
 static struct cfq_queue *cfqq_close(struct cfq_data *cfqd,
@@ -1803,7 +1792,7 @@ static bool cfq_should_idle(struct cfq_data *cfqd, struct cfq_queue *cfqq)
         * Otherwise, we do only if they are the last ones
         * in their service tree.
         */
-       return service_tree->count == 1;
+       return service_tree->count == 1 && cfq_cfqq_sync(cfqq);
 }
 
 static void cfq_arm_slice_timer(struct cfq_data *cfqd)
@@ -2976,43 +2965,20 @@ static void
 cfq_update_io_seektime(struct cfq_data *cfqd, struct cfq_queue *cfqq,
                       struct request *rq)
 {
-       sector_t sdist;
-       u64 total;
-
-       if (!cfqq->last_request_pos)
-               sdist = 0;
-       else if (cfqq->last_request_pos < blk_rq_pos(rq))
-               sdist = blk_rq_pos(rq) - cfqq->last_request_pos;
-       else
-               sdist = cfqq->last_request_pos - blk_rq_pos(rq);
+       sector_t sdist = 0;
+       sector_t n_sec = blk_rq_sectors(rq);
+       if (cfqq->last_request_pos) {
+               if (cfqq->last_request_pos < blk_rq_pos(rq))
+                       sdist = blk_rq_pos(rq) - cfqq->last_request_pos;
+               else
+                       sdist = cfqq->last_request_pos - blk_rq_pos(rq);
+       }
 
-       /*
-        * Don't allow the seek distance to get too large from the
-        * odd fragment, pagein, etc
-        */
-       if (cfqq->seek_samples <= 60) /* second&third seek */
-               sdist = min(sdist, (cfqq->seek_mean * 4) + 2*1024*1024);
+       cfqq->seek_history <<= 1;
+       if (blk_queue_nonrot(cfqd->queue))
+               cfqq->seek_history |= (n_sec < CFQQ_SECT_THR_NONROT);
        else
-               sdist = min(sdist, (cfqq->seek_mean * 4) + 2*1024*64);
-
-       cfqq->seek_samples = (7*cfqq->seek_samples + 256) / 8;
-       cfqq->seek_total = (7*cfqq->seek_total + (u64)256*sdist) / 8;
-       total = cfqq->seek_total + (cfqq->seek_samples/2);
-       do_div(total, cfqq->seek_samples);
-       cfqq->seek_mean = (sector_t)total;
-
-       /*
-        * If this cfqq is shared between multiple processes, check to
-        * make sure that those processes are still issuing I/Os within
-        * the mean seek distance.  If not, it may be time to break the
-        * queues apart again.
-        */
-       if (cfq_cfqq_coop(cfqq)) {
-               if (CFQQ_SEEKY(cfqq) && !cfqq->seeky_start)
-                       cfqq->seeky_start = jiffies;
-               else if (!CFQQ_SEEKY(cfqq))
-                       cfqq->seeky_start = 0;
-       }
+               cfqq->seek_history |= (sdist > CFQQ_SEEK_THR);
 }
 
 /*
@@ -3037,8 +3003,7 @@ cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq,
                cfq_mark_cfqq_deep(cfqq);
 
        if (!atomic_read(&cic->ioc->nr_tasks) || !cfqd->cfq_slice_idle ||
-           (!cfq_cfqq_deep(cfqq) && sample_valid(cfqq->seek_samples)
-            && CFQQ_SEEKY(cfqq)))
+           (!cfq_cfqq_deep(cfqq) && CFQQ_SEEKY(cfqq)))
                enable_idle = 0;
        else if (sample_valid(cic->ttime_samples)) {
                if (cic->ttime_mean > cfqd->cfq_slice_idle)
@@ -3453,14 +3418,6 @@ cfq_merge_cfqqs(struct cfq_data *cfqd, struct cfq_io_context *cic,
        return cic_to_cfqq(cic, 1);
 }
 
-static int should_split_cfqq(struct cfq_queue *cfqq)
-{
-       if (cfqq->seeky_start &&
-           time_after(jiffies, cfqq->seeky_start + CFQQ_COOP_TOUT))
-               return 1;
-       return 0;
-}
-
 /*
  * Returns NULL if a new cfqq should be allocated, or the old cfqq if this
  * was the last process referring to said cfqq.
@@ -3469,9 +3426,9 @@ static struct cfq_queue *
 split_cfqq(struct cfq_io_context *cic, struct cfq_queue *cfqq)
 {
        if (cfqq_process_refs(cfqq) == 1) {
-               cfqq->seeky_start = 0;
                cfqq->pid = current->pid;
                cfq_clear_cfqq_coop(cfqq);
+               cfq_clear_cfqq_split_coop(cfqq);
                return cfqq;
        }
 
@@ -3510,7 +3467,7 @@ new_queue:
                /*
                 * If the queue was seeky for too long, break it apart.
                 */
-               if (cfq_cfqq_coop(cfqq) && should_split_cfqq(cfqq)) {
+               if (cfq_cfqq_coop(cfqq) && cfq_cfqq_split_coop(cfqq)) {
                        cfq_log_cfqq(cfqd, cfqq, "breaking apart cfqq");
                        cfqq = split_cfqq(cic, cfqq);
                        if (!cfqq)