

Patch from Nick Piggin <piggin@cyberone.com.au>

Contributions from Mitchell Blank Jr <mitch@sfgoth.com>

This patch replaces the anticipatory io scheduler and smaller batch_expire
patch in mm3.  It bumps dd->anticipate to 1 when we first start anticipating,
and increments it only in the timeout.  Basically, dd->anticipate is the xth
ms we have been anticipating for.

This means we don't need "no auto unplug" stuff.



 block/deadline-iosched.c |  564 +++++++++++++++++++++++++++++++++++++----------
 1 files changed, 451 insertions(+), 113 deletions(-)

diff -puN drivers/block/deadline-iosched.c~anticipatory_io_scheduling drivers/block/deadline-iosched.c
--- 25/drivers/block/deadline-iosched.c~anticipatory_io_scheduling	2003-02-09 19:36:19.000000000 -0800
+++ 25-akpm/drivers/block/deadline-iosched.c	2003-02-09 19:36:19.000000000 -0800
@@ -19,6 +19,24 @@
 #include <linux/hash.h>
 #include <linux/rbtree.h>
 
+struct ant_stats {
+	int reads;	/* total read requests */
+	int writes;	/* total write requests */
+	int anticipate_starts;
+	int expired_read_batches;
+	int expired_write_batches;
+	int timeouts;
+	int anticipate_hits;
+
+	int ant_delay_hist[100];	/* milliseconds */
+
+	/*
+	 * This is a logarithmic (base 2) histogram
+	 */
+	int lba_forward_offsets[32];
+	int lba_backward_offsets[32];
+} ant_stats;
+
 /*
  * See Documentation/deadline-iosched.txt
  */
@@ -26,7 +44,7 @@
 /*
  * max time before a read is submitted.
  */
-static int read_expire = HZ / 2;
+static int read_expire = HZ / 20;
 
 /*
  * ditto for writes, these limits are not hard, even
@@ -38,15 +56,18 @@ static int write_expire = 5 * HZ;
  * read_batch_expire describes how long we will allow a stream of reads to
  * persist before looking to see whether it is time to switch over to writes.
  */
-static int read_batch_expire = HZ / 20;
+static int read_batch_expire = HZ / 10;
 
 /*
  * write_batch_expire describes how long we will allow a stream of writes to
  * persist before looking to see whether it is time to switch over to reads.
  */
-static int write_batch_expire = HZ / 40;
+static int write_batch_expire = HZ / 10;
 
-static int writes_starved = 2;    /* max times reads can starve a write */
+/*
+ * max time we may wait to anticipate a read
+ */
+static int antic_expire = HZ / 100;
 
 static const int deadline_hash_shift = 10;
 #define DL_HASH_BLOCK(sec)	((sec) >> 3)
@@ -78,17 +99,24 @@ struct deadline_data {
 	struct list_head *dispatch;	/* driver dispatch queue */
 	struct list_head *hash;		/* request hash */
 	unsigned long hash_valid_count;	/* barrier hash count */
-	unsigned int current_batch_expires;
+	unsigned long current_batch_expires;
+	unsigned long current_check_fifo[2];
 	int batch_data_dir;		/* current/last batch READ or WRITE */
-	unsigned int starved;		/* times reads have starved writes */
+
+	int anticipating;		/* bool: anticipating a request */
+	int antic_found;
+	unsigned long anticipate_start;	/* jiffies: when it started */
+	struct timer_list antic_timer;	/* anticipatory scheduling timer */
+	struct work_struct antic_work;	/* anticipatory scheduling work */
+	unsigned long anticipate_id;	/* Identify the expected process */
 
 	/*
 	 * settings that change how the i/o scheduler behaves
 	 */
 	int fifo_expire[2];
-	int writes_starved;
 	int batch_expire[2];
 	int front_merges;
+	int antic_expire;
 };
 
 /*
@@ -103,6 +131,8 @@ struct deadline_rq {
 
 	struct request *request;
 
+	unsigned long request_id;
+
 	/*
 	 * request hash, key is the ending offset (for back merge lookup)
 	 */
@@ -116,6 +146,14 @@ struct deadline_rq {
 	unsigned long expires;
 };
 
+/*
+ * deadline_update_drq must be called whenever a request (drq) is added to
+ * the sort_list. This function keeps caches up to date, and checks if the
+ * request might be one we are "anticipating"
+ */
+static void
+deadline_update_drq(struct deadline_data *dd, struct deadline_rq *drq);
+
 static kmem_cache_t *drq_pool;
 
 #define RQ_DATA(rq)	((struct deadline_rq *) (rq)->elevator_private)
@@ -135,7 +173,7 @@ static inline void deadline_del_drq_hash
 		__deadline_del_drq_hash(drq);
 }
 
-static inline void
+static void
 deadline_add_drq_hash(struct deadline_data *dd, struct deadline_rq *drq)
 {
 	struct request *rq = drq->request;
@@ -228,62 +266,55 @@ __deadline_add_drq_rb(struct deadline_da
 	return 0;
 }
 
+/*
+ * Add the request to the rb tree if it is unique.  If there is an alias (an
+ * existing request against the same sector) then just place this request
+ * alongside its alias on the request list.  They will be moved onto the
+ * request list at the same time.  Aliases can occur frequently when direct-io
+ * is in use.
+ */
 static int
 deadline_add_drq_rb(struct deadline_data *dd, struct deadline_rq *drq)
 {
-	struct deadline_rq *__alias;
-	const int data_dir = rq_data_dir(drq->request);
-	
-	/* keep the next_drq cache up to date */
-	if (0 && dd->next_drq[data_dir] != NULL) {
-		sector_t last = dd->last_sector[data_dir];
-		sector_t next = dd->next_drq[data_dir]->request->sector;
-		sector_t this = drq->request->sector;
-
-		/* 
-		 * We can have one of the following 2 situations. If "this"
-		 * lands on a + (or L), it is a better "next" candidate.
-		 * 
-		 * ---L+++N---
-		 * +++N---L+++
-		 */
-		if ((next > last && (this >= last && this < next))
-		 || (next < last && (this >= last || this < next)) )
-			/* we have a better "next" drq */
-			dd->next_drq[data_dir] = drq;
-	} else
-		dd->next_drq[data_dir] = drq;
+	struct deadline_rq *alias;
+	struct request *rq = drq->request;
 
-	/* now add to the rb tree */
-	drq->rb_key = rq_rb_key(drq->request);
+	drq->rb_key = rq_rb_key(rq);
 
-	__alias = __deadline_add_drq_rb(dd, drq);
-	if (!__alias) {
+	alias = __deadline_add_drq_rb(dd, drq);
+	if (alias) {
+		list_add(&rq->queuelist, &alias->request->queuelist);
+	} else {
 		rb_insert_color(&drq->rb_node, DRQ_RB_ROOT(dd, drq));
-		return 0;
+		deadline_update_drq(dd, drq);
 	}
-
-	/*
-	 * this should not typically happen, but if it does simply chain
-	 * the two requests. then they will be moved to the dispatch list
-	 * at the same time
-	 */
-	list_add(&drq->request->queuelist, &__alias->request->queuelist);
-	return 1;
+	return (alias != NULL);
 }
 
-static inline void
+static struct deadline_rq *
+deadline_choose_req(struct deadline_data *dd,
+		struct deadline_rq *drq1, struct deadline_rq *drq2);
+
+static void
 deadline_del_drq_rb(struct deadline_data *dd, struct deadline_rq *drq)
 {
 	const int data_dir = rq_data_dir(drq->request);
 
 	if (dd->next_drq[data_dir] == drq) {
 		struct rb_node *rbnext = rb_next(&drq->rb_node);
+		struct rb_node *rbprev = rb_prev(&drq->rb_node);
+		struct deadline_rq *drq_next, *drq_prev;
 
-		if (rbnext)
-			dd->next_drq[data_dir] = rb_entry_drq(rbnext);
+		if (rbprev)
+			drq_prev = rb_entry_drq(rbprev);
+		else
+			drq_prev = NULL;
+	
+		if (rbnext) 
+			drq_next = rb_entry_drq(rbnext);
 		else
-			dd->next_drq[data_dir] = deadline_find_first_drq(dd, data_dir);
+			drq_next = deadline_find_first_drq(dd, data_dir);
+		dd->next_drq[data_dir] = deadline_choose_req(dd, drq_next, drq_prev);
 	}
 
 	if (ON_RB(&drq->rb_node)) {
@@ -315,7 +346,7 @@ deadline_find_drq_rb(struct deadline_dat
 /*
  * add drq to rbtree and fifo
  */
-static inline void
+static void
 deadline_add_request(struct deadline_data *dd, struct deadline_rq *drq)
 {
 	const int data_dir = rq_data_dir(drq->request);
@@ -466,7 +497,7 @@ deadline_merged_requests(request_queue_t
 /*
  * move request from sort list to dispatch queue.
  */
-static inline void
+static void
 deadline_move_to_dispatch(struct deadline_data *dd, struct deadline_rq *drq)
 {
 	struct request *rq = drq->request;
@@ -481,6 +512,7 @@ deadline_move_to_dispatch(struct deadlin
 	list_add_tail(&drq->request->queuelist, dd->dispatch);
 }
 
+
 /*
  * move an entry to dispatch queue
  */
@@ -489,14 +521,26 @@ deadline_move_request(struct deadline_da
 {
 	const int data_dir = rq_data_dir(drq->request);
 	struct rb_node *rbnext = rb_next(&drq->rb_node);
+	struct rb_node *rbprev = rb_prev(&drq->rb_node);
+	struct deadline_rq *drq_next, *drq_prev;
 
-	if (rbnext)
-		dd->next_drq[data_dir] = rb_entry_drq(rbnext);
+	if (rbprev)
+		drq_prev = rb_entry_drq(rbprev);
+	else
+		drq_prev = NULL;
+	
+	if (rbnext) 
+		drq_next = rb_entry_drq(rbnext);
 	else
-		dd->next_drq[data_dir] = deadline_find_first_drq(dd, data_dir);
+		drq_next = deadline_find_first_drq(dd, data_dir);
+	dd->next_drq[data_dir] = deadline_choose_req(dd, drq_next, drq_prev);
 
 	dd->last_sector[data_dir] = drq->request->sector + drq->request->nr_sectors;
 
+	if (data_dir == READ)
+		/* In case we have to anticipate after this */
+		dd->anticipate_id = drq->request_id;
+
 	/*
 	 * take it off the sort and fifo list, move
 	 * to dispatch queue
@@ -507,107 +551,362 @@ deadline_move_request(struct deadline_da
 #define list_entry_fifo(ptr)	list_entry((ptr), struct deadline_rq, fifo)
 
 /*
- * deadline_check_fifo returns 0 if there are no expired reads on the fifo,
- * 1 otherwise. Requires !list_empty(&dd->fifo_list[data_dir])
+ * deadline_fifo_expired returns 0 if there are no expired reads on the fifo,
+ * 1 otherwise.
+ */ 
+static inline int deadline_fifo_expired(struct deadline_data *dd, int ddir)
+{
+	struct deadline_rq *drq;
+
+	if (list_empty(&dd->fifo_list[ddir]))
+		return 0;
+
+	drq = list_entry_fifo(dd->fifo_list[ddir].next);
+	
+	return time_after(jiffies, drq->expires);
+}
+
+/*
+ * deadline_check_fifo returns 0 if the fifo list is not due to be checked.
+ * 1 otherwise.
  */
 static inline int deadline_check_fifo(struct deadline_data *dd, int ddir)
 {
-	struct deadline_rq *drq = list_entry_fifo(dd->fifo_list[ddir].next);
+	return time_after(jiffies, dd->current_check_fifo[ddir]);
+}
+
+/*
+ * deadline_batch_expired returns true if the current batch has expired,
+ */
+static inline int deadline_batch_expired(struct deadline_data *dd)
+{
+	return time_after(jiffies, dd->current_batch_expires);
+}
+
+/*
+ * anticipatory scheduling functions follow
+ */
+
+static inline unsigned long request_id(void)
+{
+	return (unsigned long)current->pid;
+}
+
+static int deadline_queue_empty(request_queue_t *q);
+
+/*
+ * deadline_anticipate_work is scheduled by deadline_anticipate_timeout. It
+ * stops anticipation, ie. resumes dispatching requests to a device.
+ */
+static void deadline_anticipate_work(void *data)
+{
+	struct request_queue *q = data;
+	struct deadline_data *dd = q->elevator.elevator_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	dd->anticipating = 0;
+	blk_remove_plug(q);
+	if (!deadline_queue_empty(q))
+		q->request_fn(q);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+}
+
+/*
+ * deadline_anticipate_timeout is the timer function set by
+ * deadline_start_anticipate.
+ */
+static void deadline_anticipate_timeout(unsigned long data)
+{
+	struct request_queue *q = (struct request_queue *)data;
+	struct deadline_data *dd = q->elevator.elevator_data;
+
+	dd->batch_data_dir = WRITE;
+	schedule_work(&dd->antic_work);
+	ant_stats.timeouts++;
+}
+
+#define MAXBACK (512 * 1024)
+
+/*
+ * deadline_close_req decides if one request is considered "close" to the next.
+ * The allowable distance between close requests is exponentially related to
+ * @factor. @factor is only useful between about 1 and 10, it is used to
+ * increase our tolerance of a useful request the longer we anticipate one.
+ */
+static int
+deadline_close_req(sector_t last, sector_t next, int factor)
+{
+	sector_t delta, backdelta;	/* acceptable close offset (in sectors) */
+	switch (factor) {
+		case 0:
+		case 1:
+			delta = 16;
+			break;
+		case 2:
+			delta = 64;
+			break;
+		case 3:
+			delta = 1024;
+			break;
+		case 4:
+			delta = 16 * 1024;
+			break;
+		case 5:
+			delta = 256 * 1024;
+			break;
+		case 6:
+			delta = 4 * 1024 * 1024;
+			break;
+		default:
+			return 1;
+			
+	}
+
+	backdelta = min_t(sector_t, MAXBACK, delta);
 
 	/*
-	 * drq is expired!
+	 * Forward seeks are favoured to bias the elevator in the
+	 * "upward" direction. This is questionable.
 	 */
-	if (time_after(jiffies, drq->expires))
+	return (last - backdelta <= next) && (next <= last + delta);
+}
+
+static struct deadline_rq *
+deadline_choose_req(struct deadline_data *dd,
+		struct deadline_rq *drq1, struct deadline_rq *drq2)
+{
+	int data_dir;
+	sector_t last, s1, s2, d1, d2;
+	const sector_t maxback = MAXBACK;
+	sector_t highnum = 0;
+	highnum -= 1;
+	highnum /= 2;
+
+	if (drq1 == NULL)
+		return drq2;
+	if (drq2 == NULL)
+		return drq1;
+
+	data_dir = rq_data_dir(drq1->request);
+	last = dd->last_sector[data_dir];
+	s1 = drq1->request->sector;
+	s2 = drq2->request->sector;
+
+	BUG_ON(data_dir != rq_data_dir(drq2->request));
+
+	if (s1 >= last)
+		d1 = s1 - last;
+	else {
+		/* count large back seeks as a forward seek */
+		if (s1+maxback >= last)
+			d1 = last - s1;
+		else
+			d1 = highnum-last+s1; /* TODO should be (maxdisk-last)+s1; */
+	}
+
+	if (s2 >= last)
+		d2 = s2 - last;
+	else {
+		if (s2+maxback >= last)
+			d2 = last - s2;
+		else
+			d2 = highnum-last+s2;
+	}
+
+	if (d1 < d2)
+		return drq1;
+	else if (d2 < d1)
+		return drq2;
+	else {
+		if (s1 >= s2)
+			return drq1;
+		else
+			return drq2;
+	}
+}
+
+/*
+ * deadline_antic_req, has @dd been anticipating this @drq?
+ */
+static int
+deadline_antic_req(struct deadline_data *dd, struct deadline_rq *drq)
+{
+	unsigned long delay = jiffies - dd->anticipate_start;
+	sector_t last = dd->last_sector[READ];
+	sector_t next = drq->request->sector;
+	
+	if (deadline_close_req(last, next, delay)
+		|| dd->anticipate_id == drq->request_id)
 		return 1;
 
 	return 0;
 }
 
 /*
- * deadline_check_batch returns 0 if the current batch has not expired,
- * 1 otherwise.
+ * deadline_update_drq must be called whenever a request (drq) is added to
+ * the sort_list. This function keeps caches up to date, and checks if the
+ * request might be one we are "anticipating"
  */
-static inline int deadline_batch_expired(struct deadline_data *dd)
+static void
+deadline_update_drq(struct deadline_data *dd, struct deadline_rq *drq)
 {
-	return time_after(jiffies, dd->current_batch_expires);
+	const int data_dir = rq_data_dir(drq->request);
+	sector_t last = dd->last_sector[data_dir];
+	sector_t this = drq->request->sector;
+	unsigned long delay = jiffies - dd->anticipate_start;
+
+	drq->request_id = request_id();
+
+	if (data_dir == READ)
+		ant_stats.reads++;
+	else
+		ant_stats.writes++;
+
+	/* keep the next_drq cache up to date */
+	dd->next_drq[data_dir] = deadline_choose_req(dd, drq, dd->next_drq[data_dir]);
+
+	/* have we been anticipating this request? */
+	if (dd->anticipating && data_dir == READ && deadline_antic_req(dd, drq)) {
+		long lba_offset;
+		int neg;
+		int log2;
+
+		if (delay >= ARRAY_SIZE(ant_stats.ant_delay_hist))
+			delay = ARRAY_SIZE(ant_stats.ant_delay_hist) - 1;
+		ant_stats.ant_delay_hist[delay]++;
+		ant_stats.anticipate_hits++;
+
+		lba_offset = this - last;
+		neg = 0;
+		if (lba_offset < 0) {
+			lba_offset = -lba_offset;
+			neg = 1;
+		}
+		log2 = ffs(lba_offset);
+		BUG_ON(log2 >= 32);
+		if (neg)
+			ant_stats.lba_backward_offsets[log2]++;
+		else
+			ant_stats.lba_forward_offsets[log2]++;
+
+		dd->antic_found = 1;
+		del_timer(&dd->antic_timer);
+		schedule_work(&dd->antic_work);
+	}
 }
 
 /*
- * deadline_dispatch_requests selects the best request according to
- * read/write expire, batch expire, etc
+ * deadline_dispatch_request selects the best request according to
+ * read/write expire, batch expire, etc, and moves it to the dispatch
+ * queue. Returns 1 if a request was found, 0 otherwise.
  */
-static int deadline_dispatch_requests(struct deadline_data *dd)
+static int deadline_dispatch_request(struct request_queue *q)
 {
+	struct deadline_data *dd = q->elevator.elevator_data;
+	struct deadline_rq *drq;
 	const int reads = !list_empty(&dd->fifo_list[READ]);
 	const int writes = !list_empty(&dd->fifo_list[WRITE]);
-	struct deadline_rq *drq;
-	int data_dir, other_dir;
 
-	/*
-	 * batches are reads XOR writes
-	 */
-	drq = dd->next_drq[dd->batch_data_dir];
+	if (deadline_batch_expired(dd)) {
+		if (dd->batch_data_dir == READ)
+			ant_stats.expired_read_batches++;
+		else
+			ant_stats.expired_write_batches++;
+	}
 
-	if (drq && !deadline_batch_expired(dd))
-		/* we have a "next request" and our batch is still running */
-		goto dispatch_request;
+	if (!(reads && writes && deadline_batch_expired(dd)) ) {
+		/*
+		 * batch is still running or no reads or no writes
+		 */
+		drq = dd->next_drq[dd->batch_data_dir];
+
+		if (dd->batch_data_dir == READ
+		    && (!drq || !deadline_antic_req(dd, drq) || dd->antic_found)) {
+			unsigned long timeout;
+			
+			if (deadline_check_fifo(dd, READ)) {
+				if (deadline_fifo_expired(dd, READ))
+					goto dispatch_request;
+				
+				dd->current_check_fifo[READ] = jiffies +
+					dd->fifo_expire[READ] / 2;
+			}
+					
+			timeout = min(jiffies + dd->antic_expire,
+					dd->current_batch_expires);
+			timeout = min(timeout, dd->current_check_fifo[READ]);
+
+			ant_stats.anticipate_starts++;
+			blk_plug_device(q);
+			dd->anticipating = 1;
+			dd->antic_found = 0;
+			dd->anticipate_start = jiffies;
+			mod_timer(&dd->antic_timer, timeout);
+			return 0;
+		}
+		if (drq)
+			/* we have a "next request" */
+			goto dispatch_request;
+	}
 
 	/*
 	 * at this point we are not running a batch. select the appropriate
 	 * data direction (read / write)
 	 */
-	
-	dd->current_batch_expires = jiffies + dd->batch_expire[WRITE];
 
 	if (reads) {
 		BUG_ON(RB_EMPTY(&dd->sort_list[READ]));
 
-		if (writes && (dd->starved++ >= dd->writes_starved))
+		if (writes && dd->batch_data_dir == READ)
+			/*
+			 * Last batch was a read, switch to writes
+			 */
 			goto dispatch_writes;
 
-		data_dir = dd->batch_data_dir = READ;
-		dd->current_batch_expires = jiffies + dd->batch_expire[READ];
-		other_dir = WRITE;
-
-		goto dispatch_find_request;
+		dd->batch_data_dir = READ;
+		drq = dd->next_drq[dd->batch_data_dir];
+		dd->current_batch_expires = jiffies +
+			dd->batch_expire[dd->batch_data_dir];
+		goto dispatch_request;
 	}
 
 	/*
-	 * there are either no reads or writes have been starved
+	 * there are either no reads or the last batch was a read
 	 */
 
 	if (writes) {
 dispatch_writes:
 		BUG_ON(RB_EMPTY(&dd->sort_list[WRITE]));
 
-		dd->starved = 0;
-
-		data_dir = dd->batch_data_dir = WRITE;
-		other_dir = READ;
-
-		goto dispatch_find_request;
+		dd->batch_data_dir = WRITE;
+		drq = dd->next_drq[dd->batch_data_dir];
+		dd->current_batch_expires = jiffies +
+			dd->batch_expire[dd->batch_data_dir];
+		goto dispatch_request;
 	}
 
 	return 0;
 
-dispatch_find_request:
+dispatch_request:
 	/*
-	 * we are not running a batch, find best request for selected data_dir
+	 * check fifo if it is due
 	 */
-	if (deadline_check_fifo(dd, data_dir)) 
-		/* An expired request exists - satisfy it */
-		drq = list_entry_fifo(dd->fifo_list[data_dir].next);
-	
-	else
-		/* No expired requests, get the next in sort order */
-		drq = dd->next_drq[data_dir];
+	if (deadline_check_fifo(dd, dd->batch_data_dir)) {
+		/* reset timer to check twice per expire interval */
+		dd->current_check_fifo[dd->batch_data_dir] = jiffies +
+			dd->fifo_expire[dd->batch_data_dir] / 2;
+		
+		if (deadline_fifo_expired(dd, dd->batch_data_dir)) 
+			/* An expired request exists - satisfy it */
+			drq = list_entry_fifo(
+				dd->fifo_list[dd->batch_data_dir].next);
+	}
 
-dispatch_request:
 	/*
 	 * drq is the selected appropriate request.
 	 */
 	deadline_move_request(dd, drq);
-
 	return 1;
 }
 
@@ -625,7 +924,7 @@ dispatch:
 		return rq;
 	}
 
-	if (deadline_dispatch_requests(dd))
+	if (deadline_dispatch_request(q))
 		goto dispatch;
 
 	return NULL;
@@ -648,6 +947,12 @@ deadline_insert_request(request_queue_t 
 			insert_here = dd->dispatch->prev;
 
 		list_add(&rq->queuelist, insert_here);
+		
+		if (dd->anticipating) {
+			del_timer(&dd->antic_timer);
+			schedule_work(&dd->antic_work);
+		}
+		
 		return;
 	}
 
@@ -666,13 +971,40 @@ static int deadline_queue_empty(request_
 	struct deadline_data *dd = q->elevator.elevator_data;
 
 	if (!list_empty(&dd->fifo_list[WRITE])
-	    || !list_empty(&dd->fifo_list[READ])
-	    || !list_empty(dd->dispatch))
-		return 0;
+		|| !list_empty(&dd->fifo_list[READ])
+		|| !list_empty(dd->dispatch) )
+			return 0;
 
 	return 1;
 }
 
+/*
+ * deadline_queue_notready tells us weather or not deadline_next_request
+ * will return us a request or NULL. With the previous work conserving
+ * scheduler this API was designed around, if a queue had requests in it,
+ * deadline_next_request would return a request, and drivers seem to make
+ * that assumption
+ */
+static int deadline_queue_notready(request_queue_t *q)
+{
+	struct deadline_data *dd = q->elevator.elevator_data;
+
+	if (!list_empty(dd->dispatch))
+		return 0;
+	
+	if (dd->anticipating)
+		return 1;
+	
+	if (list_empty(&dd->fifo_list[WRITE]) 
+		&& list_empty(&dd->fifo_list[READ]) )
+		return 1;
+				
+	if (!deadline_dispatch_request(q))
+		return 1;
+
+	return 0;
+}
+
 static struct request *
 deadline_former_request(request_queue_t *q, struct request *rq)
 {
@@ -751,6 +1083,12 @@ static int deadline_init(request_queue_t
 		return -ENOMEM;
 	}
 
+	/* anticipatory scheduling helpers */
+	dd->antic_timer.function = deadline_anticipate_timeout;
+	dd->antic_timer.data = (unsigned long)q;
+	init_timer(&dd->antic_timer);
+	INIT_WORK(&dd->antic_work, deadline_anticipate_work, q);
+
 	for (i = 0; i < DL_HASH_ENTRIES; i++)
 		INIT_LIST_HEAD(&dd->hash[i]);
 
@@ -762,8 +1100,8 @@ static int deadline_init(request_queue_t
 	dd->fifo_expire[READ] = read_expire;
 	dd->fifo_expire[WRITE] = write_expire;
 	dd->hash_valid_count = 1;
-	dd->writes_starved = writes_starved;
 	dd->front_merges = 1;
+	dd->antic_expire = antic_expire;
 	dd->batch_expire[READ] = read_batch_expire;
 	dd->batch_expire[WRITE] = write_batch_expire;
 	e->elevator_data = dd;
@@ -827,8 +1165,8 @@ static ssize_t __FUNC(struct deadline_da
 }
 SHOW_FUNCTION(deadline_readexpire_show, dd->fifo_expire[READ]);
 SHOW_FUNCTION(deadline_writeexpire_show, dd->fifo_expire[WRITE]);
-SHOW_FUNCTION(deadline_writesstarved_show, dd->writes_starved);
 SHOW_FUNCTION(deadline_frontmerges_show, dd->front_merges);
+SHOW_FUNCTION(deadline_anticexpire_show, dd->antic_expire);
 SHOW_FUNCTION(deadline_read_batchexpire_show, dd->batch_expire[READ]);
 SHOW_FUNCTION(deadline_write_batchexpire_show, dd->batch_expire[WRITE]);
 #undef SHOW_FUNCTION
@@ -845,8 +1183,8 @@ static ssize_t __FUNC(struct deadline_da
 }
 STORE_FUNCTION(deadline_readexpire_store, &dd->fifo_expire[READ], 0, INT_MAX);
 STORE_FUNCTION(deadline_writeexpire_store, &dd->fifo_expire[WRITE], 0, INT_MAX);
-STORE_FUNCTION(deadline_writesstarved_store, &dd->writes_starved, INT_MIN, INT_MAX);
 STORE_FUNCTION(deadline_frontmerges_store, &dd->front_merges, 0, 1);
+STORE_FUNCTION(deadline_anticexpire_store, &dd->antic_expire, 0, INT_MAX);
 STORE_FUNCTION(deadline_read_batchexpire_store,
 			&dd->batch_expire[READ], 0, INT_MAX);
 STORE_FUNCTION(deadline_write_batchexpire_store,
@@ -863,16 +1201,16 @@ static struct deadline_fs_entry deadline
 	.show = deadline_writeexpire_show,
 	.store = deadline_writeexpire_store,
 };
-static struct deadline_fs_entry deadline_writesstarved_entry = {
-	.attr = {.name = "writes_starved", .mode = S_IRUGO | S_IWUSR },
-	.show = deadline_writesstarved_show,
-	.store = deadline_writesstarved_store,
-};
 static struct deadline_fs_entry deadline_frontmerges_entry = {
 	.attr = {.name = "front_merges", .mode = S_IRUGO | S_IWUSR },
 	.show = deadline_frontmerges_show,
 	.store = deadline_frontmerges_store,
 };
+static struct deadline_fs_entry deadline_anticexpire_entry = {
+	.attr = {.name = "antic_expire", .mode = S_IRUGO | S_IWUSR },
+	.show = deadline_anticexpire_show,
+	.store = deadline_anticexpire_store,
+};
 static struct deadline_fs_entry deadline_read_batchexpire_entry = {
 	.attr = {.name = "read_batch_expire", .mode = S_IRUGO | S_IWUSR },
 	.show = deadline_read_batchexpire_show,
@@ -887,8 +1225,8 @@ static struct deadline_fs_entry deadline
 static struct attribute *default_attrs[] = {
 	&deadline_readexpire_entry.attr,
 	&deadline_writeexpire_entry.attr,
-	&deadline_writesstarved_entry.attr,
 	&deadline_frontmerges_entry.attr,
+	&deadline_anticexpire_entry.attr,
 	&deadline_read_batchexpire_entry.attr,
 	&deadline_write_batchexpire_entry.attr,
 	NULL,
@@ -951,7 +1289,7 @@ elevator_t iosched_deadline = {
 	.elevator_next_req_fn =		deadline_next_request,
 	.elevator_add_req_fn =		deadline_insert_request,
 	.elevator_remove_req_fn =	deadline_remove_request,
-	.elevator_queue_empty_fn =	deadline_queue_empty,
+	.elevator_queue_empty_fn =	deadline_queue_notready,
 	.elevator_former_req_fn =	deadline_former_request,
 	.elevator_latter_req_fn =	deadline_latter_request,
 	.elevator_init_fn =		deadline_init,

_
