Subject: [PATCH] AS seek distance
From: Nick Piggin <[email-masked]>
To: [email-masked]
Date: Tue, 6 May 2003 14:13:27 +1000

Hi Andrew,
This patch monitors each process's seek distance,
and will stop anticipation if we get a request closer
than we're likely to get from the process we're waiting
on. It does alright on tests. Gives OraSim a bit of
improvement.

It should really be calculating the _time_ each seeks
are taking. That is quite difficult though, because
we also need to take into account the time it takes
to actually transfer the request. I might work on this.

The patch also undoes the restriction that only
requests from the same process may seek backward.
This does not seem to hurt fairness on my tests. It
is about trying to close the gap between AS and DL
for TCQ.

Nick
--- linux/drivers/block/as-iosched.c.orig	2003-05-06 13:29:40.000000000 +1000
+++ linux/drivers/block/as-iosched.c	2003-05-06 13:57:56.000000000 +1000
@@ -88,6 +88,11 @@ struct as_io_context {
 	int last_data_dir;
 	unsigned long dir_after_read[2];
 	int mean_dir_after_read;
+	/* Layout pattern */
+	sector_t last_request_pos;
+	sector_t seek_total;
+	sector_t seek_samples;
+	sector_t seek_mean;
 };
 
 /* Bits in as_io_context.state */
@@ -254,6 +259,9 @@ static struct as_io_context *get_as_io_c
 			ret->dir_after_read[READ] = 0;
 			ret->dir_after_read[WRITE] = 0;
 			ret->mean_dir_after_read = READ;
+			ret->seek_total = 0;
+			ret->seek_samples = 0;
+			ret->seek_mean = 0;
 			tsk->as_io_context = ret;
 		}
 	}
@@ -455,9 +463,10 @@ as_find_arq_rb(struct as_data *ad, secto
  * IO Scheduler proper
  */
 
-#define MAXBACK (512 * 1024)	/* Maximum distance a process can seek backward
-				   from a previous request it has made. No
-				   seeking backward between processes allowed */
+#define MAXBACK (1024 * 1024)	/*
+				 * Maximum distance the disk will go backward
+				 * for a request.
+				 */
 
 /*
  * as_choose_req selects the preferred one of two requests of the same data_dir
@@ -487,13 +496,11 @@ as_choose_req(struct as_data *ad, struct
 	/*
 	 * Strict one way elevator _except_ in the case where we allow
 	 * short backward seeks which are biased as twice the cost of a
-	 * similar forward seek. Only for reads and only between reads
-	 * from the same process!
+	 * similar forward seek.
 	 */
 	if (s1 >= last)
 		d1 = s1 - last;
 	else if (data_dir == READ
-			&& ad->as_io_context == arq1->as_io_context
 			&& s1+maxback >= last)
 				d1 = (last - s1)*2;
 	else {
@@ -504,7 +511,6 @@ as_choose_req(struct as_data *ad, struct
 	if (s2 >= last)
 		d2 = s2 - last;
 	else if (data_dir == READ
-			&& ad->as_io_context == arq2->as_io_context
 			&& s2+maxback >= last)
 				d2 = (last - s2)*2;
 	else {
@@ -724,30 +730,46 @@ static int as_can_break_anticipation(str
 		/* request from same process */
 		return 1;
 	}
-	if (aic && !test_bit(AS_TASK_RUNNING, &aic->state)) {
+
+	if (!aic)
+		return 0;
+	
+	if (!test_bit(AS_TASK_RUNNING, &aic->state)) {
 		/* process anticipated on has exitted */
 		return 1;
 	}
 
-	if (aic && atomic_read(&aic->nr_queued) > 0) {
+	if (atomic_read(&aic->nr_queued) > 0) {
 		/* process has more requests queued */
 		return 1;
 	}
 
-	if (aic && atomic_read(&aic->nr_dispatched) > 0) {
+	if (atomic_read(&aic->nr_dispatched) > 0) {
 		/* process has more requests dispatched */
 		return 1;
 	}
 
-	if (aic && aic->ttime_mean > ad->antic_expire) {
+	if (aic->ttime_mean > ad->antic_expire) {
 		/* the process thinks too much between requests */
 		return 1;
 	}
 
-	if (aic && aic->mean_dir_after_read != READ) {
+	if (aic->mean_dir_after_read != READ) {
 		/* next request from this process will probably be a write */
 		return 1;
 	}
+
+	if (aic->seek_samples) {
+		sector_t s;
+		if (ad->last_sector[READ] < arq->request->sector)
+			s = arq->request->sector - ad->last_sector[READ];
+		else
+			s = ad->last_sector[READ] - arq->request->sector;
+
+		if (aic->seek_mean > s)
+			/* this request is better than what we're expecting */
+			return 1;
+	}
 	
 	return 0;
 }
@@ -793,9 +815,11 @@ static int as_can_anticipate(struct as_d
  * updates @aic->ttime_mean based on that. It is called when a new
  * request is queued.
  */
-static void as_update_iohist(struct as_io_context *aic, int data_dir)
+static void as_update_iohist(struct as_io_context *aic, struct request *rq)
 {
+	int data_dir = rq_data_dir(rq);
 	unsigned long thinktime;
+	sector_t seek_dist;
 
 	if (aic == NULL)
 		return;
@@ -816,6 +840,24 @@ static void as_update_iohist(struct as_i
 						+ (aic->ttime_samples>>2);
 			aic->ttime_total = (aic->ttime_total>>1)
 						+ (aic->ttime_total>>2);
+
+			/* Calculate read -> read seek distance */
+			if (aic->last_request_pos < rq->sector)
+				seek_dist = rq->sector - aic->last_request_pos;
+			else
+				seek_dist = aic->last_request_pos - rq->sector;
+			aic->last_request_pos = rq->sector + rq->nr_sectors;
+			
+			aic->seek_samples += 256;
+			aic->seek_total += 256*seek_dist;
+			if (aic->seek_samples)
+				aic->seek_mean = (aic->seek_total + 128)
+							/ aic->seek_samples;
+			aic->seek_samples = (aic->seek_samples>>1)
+						+ (aic->seek_samples>>2);
+			aic->seek_total = (aic->seek_total>>1)
+						+ (aic->seek_total>>2);
+			
 		}
 
 		/* Calculate read/write pattern */
@@ -1187,7 +1229,7 @@ static void as_add_request(struct as_dat
 
 	if (arq->as_io_context) {
 		atomic_inc(&arq->as_io_context->nr_queued);
-		as_update_iohist(arq->as_io_context, data_dir);
+		as_update_iohist(arq->as_io_context, arq->request);
 	}
 
 	as_add_arq_rb(ad, arq);