[PATCH] as-iosched: migrate to using the elevator rb functions
[safe/jmp/linux-2.6] / block / as-iosched.c
index c6744ff..134545b 100644 (file)
@@ -1,10 +1,8 @@
 /*
- *  linux/drivers/block/as-iosched.c
- *
  *  Anticipatory & deadline i/o scheduler.
  *
  *  Copyright (C) 2002 Jens Axboe <axboe@suse.de>
- *                     Nick Piggin <piggin@cyberone.com.au>
+ *                     Nick Piggin <nickpiggin@yahoo.com.au>
  *
  */
 #include <linux/kernel.h>
 #include <linux/blkdev.h>
 #include <linux/elevator.h>
 #include <linux/bio.h>
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/compiler.h>
-#include <linux/hash.h>
 #include <linux/rbtree.h>
 #include <linux/interrupt.h>
 
@@ -69,7 +65,7 @@
 
 /* Bits in as_io_context.state */
 enum as_io_states {
-       AS_TASK_RUNNING=0,      /* Process has not exitted */
+       AS_TASK_RUNNING=0,      /* Process has not exited */
        AS_TASK_IOSTARTED,      /* Process has started some IO */
        AS_TASK_IORUNNING,      /* Process has completed some IO */
 };
@@ -98,10 +94,12 @@ struct as_data {
 
        struct as_rq *next_arq[2];      /* next in sort order */
        sector_t last_sector[2];        /* last REQ_SYNC & REQ_ASYNC sectors */
-       struct list_head *hash;         /* request hash */
 
        unsigned long exit_prob;        /* probability a task will exit while
                                           being waited on */
+       unsigned long exit_no_coop;     /* probablility an exited task will
+                                          not be part of a later cooperating
+                                          request */
        unsigned long new_ttime_total;  /* mean thinktime on new proc */
        unsigned long new_ttime_mean;
        u64 new_seek_total;             /* mean seek on new proc */
@@ -151,23 +149,11 @@ enum arq_state {
 };
 
 struct as_rq {
-       /*
-        * rbtree index, key is the starting offset
-        */
-       struct rb_node rb_node;
-       sector_t rb_key;
-
        struct request *request;
 
        struct io_context *io_context;  /* The submitting task */
 
        /*
-        * request hash, key is the ending offset (for back merge lookup)
-        */
-       struct list_head hash;
-       unsigned int on_hash;
-
-       /*
         * expire fifo
         */
        struct list_head fifo;
@@ -181,6 +167,12 @@ struct as_rq {
 
 static kmem_cache_t *arq_pool;
 
+static atomic_t ioc_count = ATOMIC_INIT(0);
+static struct completion *ioc_gone;
+
+static void as_move_to_dispatch(struct as_data *ad, struct as_rq *arq);
+static void as_antic_stop(struct as_data *ad);
+
 /*
  * IO Context helper functions
  */
@@ -189,6 +181,15 @@ static kmem_cache_t *arq_pool;
 static void free_as_io_context(struct as_io_context *aic)
 {
        kfree(aic);
+       if (atomic_dec_and_test(&ioc_count) && ioc_gone)
+               complete(ioc_gone);
+}
+
+static void as_trim(struct io_context *ioc)
+{
+       if (ioc->aic)
+               free_as_io_context(ioc->aic);
+       ioc->aic = NULL;
 }
 
 /* Called when the task exits */
@@ -216,6 +217,7 @@ static struct as_io_context *alloc_as_io_context(void)
                ret->seek_total = 0;
                ret->seek_samples = 0;
                ret->seek_mean = 0;
+               atomic_inc(&ioc_count);
        }
 
        return ret;
@@ -258,173 +260,24 @@ static void as_put_io_context(struct as_rq *arq)
 }
 
 /*
- * the back merge hash support functions
- */
-static const int as_hash_shift = 6;
-#define AS_HASH_BLOCK(sec)     ((sec) >> 3)
-#define AS_HASH_FN(sec)                (hash_long(AS_HASH_BLOCK((sec)), as_hash_shift))
-#define AS_HASH_ENTRIES                (1 << as_hash_shift)
-#define rq_hash_key(rq)                ((rq)->sector + (rq)->nr_sectors)
-#define list_entry_hash(ptr)   list_entry((ptr), struct as_rq, hash)
-
-static inline void __as_del_arq_hash(struct as_rq *arq)
-{
-       arq->on_hash = 0;
-       list_del_init(&arq->hash);
-}
-
-static inline void as_del_arq_hash(struct as_rq *arq)
-{
-       if (arq->on_hash)
-               __as_del_arq_hash(arq);
-}
-
-static void as_add_arq_hash(struct as_data *ad, struct as_rq *arq)
-{
-       struct request *rq = arq->request;
-
-       BUG_ON(arq->on_hash);
-
-       arq->on_hash = 1;
-       list_add(&arq->hash, &ad->hash[AS_HASH_FN(rq_hash_key(rq))]);
-}
-
-/*
- * move hot entry to front of chain
- */
-static inline void as_hot_arq_hash(struct as_data *ad, struct as_rq *arq)
-{
-       struct request *rq = arq->request;
-       struct list_head *head = &ad->hash[AS_HASH_FN(rq_hash_key(rq))];
-
-       if (!arq->on_hash) {
-               WARN_ON(1);
-               return;
-       }
-
-       if (arq->hash.prev != head) {
-               list_del(&arq->hash);
-               list_add(&arq->hash, head);
-       }
-}
-
-static struct request *as_find_arq_hash(struct as_data *ad, sector_t offset)
-{
-       struct list_head *hash_list = &ad->hash[AS_HASH_FN(offset)];
-       struct list_head *entry, *next = hash_list->next;
-
-       while ((entry = next) != hash_list) {
-               struct as_rq *arq = list_entry_hash(entry);
-               struct request *__rq = arq->request;
-
-               next = entry->next;
-
-               BUG_ON(!arq->on_hash);
-
-               if (!rq_mergeable(__rq)) {
-                       as_del_arq_hash(arq);
-                       continue;
-               }
-
-               if (rq_hash_key(__rq) == offset)
-                       return __rq;
-       }
-
-       return NULL;
-}
-
-/*
  * rb tree support functions
  */
-#define RB_NONE                (2)
-#define RB_EMPTY(root) ((root)->rb_node == NULL)
-#define ON_RB(node)    ((node)->rb_color != RB_NONE)
-#define RB_CLEAR(node) ((node)->rb_color = RB_NONE)
-#define rb_entry_arq(node)     rb_entry((node), struct as_rq, rb_node)
 #define ARQ_RB_ROOT(ad, arq)   (&(ad)->sort_list[(arq)->is_sync])
-#define rq_rb_key(rq)          (rq)->sector
 
-/*
- * as_find_first_arq finds the first (lowest sector numbered) request
- * for the specified data_dir. Used to sweep back to the start of the disk
- * (1-way elevator) after we process the last (highest sector) request.
- */
-static struct as_rq *as_find_first_arq(struct as_data *ad, int data_dir)
+static void as_add_arq_rb(struct as_data *ad, struct request *rq)
 {
-       struct rb_node *n = ad->sort_list[data_dir].rb_node;
-
-       if (n == NULL)
-               return NULL;
-
-       for (;;) {
-               if (n->rb_left == NULL)
-                       return rb_entry_arq(n);
-
-               n = n->rb_left;
-       }
-}
-
-/*
- * Add the request to the rb tree if it is unique.  If there is an alias (an
- * existing request against the same sector), which can happen when using
- * direct IO, then return the alias.
- */
-static struct as_rq *as_add_arq_rb(struct as_data *ad, struct as_rq *arq)
-{
-       struct rb_node **p = &ARQ_RB_ROOT(ad, arq)->rb_node;
-       struct rb_node *parent = NULL;
-       struct as_rq *__arq;
-       struct request *rq = arq->request;
-
-       arq->rb_key = rq_rb_key(rq);
-
-       while (*p) {
-               parent = *p;
-               __arq = rb_entry_arq(parent);
-
-               if (arq->rb_key < __arq->rb_key)
-                       p = &(*p)->rb_left;
-               else if (arq->rb_key > __arq->rb_key)
-                       p = &(*p)->rb_right;
-               else
-                       return __arq;
-       }
-
-       rb_link_node(&arq->rb_node, parent, p);
-       rb_insert_color(&arq->rb_node, ARQ_RB_ROOT(ad, arq));
-
-       return NULL;
-}
+       struct as_rq *arq = RQ_DATA(rq);
+       struct request *alias;
 
-static inline void as_del_arq_rb(struct as_data *ad, struct as_rq *arq)
-{
-       if (!ON_RB(&arq->rb_node)) {
-               WARN_ON(1);
-               return;
+       while ((unlikely(alias = elv_rb_add(ARQ_RB_ROOT(ad, arq), rq)))) {
+               as_move_to_dispatch(ad, RQ_DATA(alias));
+               as_antic_stop(ad);
        }
-
-       rb_erase(&arq->rb_node, ARQ_RB_ROOT(ad, arq));
-       RB_CLEAR(&arq->rb_node);
 }
 
-static struct request *
-as_find_arq_rb(struct as_data *ad, sector_t sector, int data_dir)
+static inline void as_del_arq_rb(struct as_data *ad, struct request *rq)
 {
-       struct rb_node *n = ad->sort_list[data_dir].rb_node;
-       struct as_rq *arq;
-
-       while (n) {
-               arq = rb_entry_arq(n);
-
-               if (sector < arq->rb_key)
-                       n = n->rb_left;
-               else if (sector > arq->rb_key)
-                       n = n->rb_right;
-               else
-                       return arq->request;
-       }
-
-       return NULL;
+       elv_rb_del(ARQ_RB_ROOT(ad, RQ_DATA(rq)), rq);
 }
 
 /*
@@ -517,32 +370,29 @@ as_choose_req(struct as_data *ad, struct as_rq *arq1, struct as_rq *arq2)
  * this with as_choose_req form the basis for how the scheduler chooses
  * what request to process next. Anticipation works on top of this.
  */
-static struct as_rq *as_find_next_arq(struct as_data *ad, struct as_rq *last)
+static struct as_rq *as_find_next_arq(struct as_data *ad, struct as_rq *arq)
 {
-       const int data_dir = last->is_sync;
-       struct as_rq *ret;
+       struct request *last = arq->request;
        struct rb_node *rbnext = rb_next(&last->rb_node);
        struct rb_node *rbprev = rb_prev(&last->rb_node);
-       struct as_rq *arq_next, *arq_prev;
+       struct as_rq *next = NULL, *prev = NULL;
 
-       BUG_ON(!ON_RB(&last->rb_node));
+       BUG_ON(RB_EMPTY_NODE(&last->rb_node));
 
        if (rbprev)
-               arq_prev = rb_entry_arq(rbprev);
-       else
-               arq_prev = NULL;
+               prev = RQ_DATA(rb_entry_rq(rbprev));
 
        if (rbnext)
-               arq_next = rb_entry_arq(rbnext);
+               next = RQ_DATA(rb_entry_rq(rbnext));
        else {
-               arq_next = as_find_first_arq(ad, data_dir);
-               if (arq_next == last)
-                       arq_next = NULL;
-       }
+               const int data_dir = arq->is_sync;
 
-       ret = as_choose_req(ad, arq_next, arq_prev);
+               rbnext = rb_first(&ad->sort_list[data_dir]);
+               if (rbnext && rbnext != &last->rb_node)
+                       next = RQ_DATA(rb_entry_rq(rbnext));
+       }
 
-       return ret;
+       return as_choose_req(ad, next, prev);
 }
 
 /*
@@ -636,37 +486,152 @@ static void as_antic_timeout(unsigned long data)
                kblockd_schedule_work(&ad->antic_work);
 
                if (aic->ttime_samples == 0) {
-                       /* process anticipated on has exitted or timed out*/
+                       /* process anticipated on has exited or timed out*/
                        ad->exit_prob = (7*ad->exit_prob + 256)/8;
                }
+               if (!test_bit(AS_TASK_RUNNING, &aic->state)) {
+                       /* process not "saved" by a cooperating request */
+                       ad->exit_no_coop = (7*ad->exit_no_coop + 256)/8;
+               }
        }
        spin_unlock_irqrestore(q->queue_lock, flags);
 }
 
+static void as_update_thinktime(struct as_data *ad, struct as_io_context *aic,
+                               unsigned long ttime)
+{
+       /* fixed point: 1.0 == 1<<8 */
+       if (aic->ttime_samples == 0) {
+               ad->new_ttime_total = (7*ad->new_ttime_total + 256*ttime) / 8;
+               ad->new_ttime_mean = ad->new_ttime_total / 256;
+
+               ad->exit_prob = (7*ad->exit_prob)/8;
+       }
+       aic->ttime_samples = (7*aic->ttime_samples + 256) / 8;
+       aic->ttime_total = (7*aic->ttime_total + 256*ttime) / 8;
+       aic->ttime_mean = (aic->ttime_total + 128) / aic->ttime_samples;
+}
+
+static void as_update_seekdist(struct as_data *ad, struct as_io_context *aic,
+                               sector_t sdist)
+{
+       u64 total;
+
+       if (aic->seek_samples == 0) {
+               ad->new_seek_total = (7*ad->new_seek_total + 256*(u64)sdist)/8;
+               ad->new_seek_mean = ad->new_seek_total / 256;
+       }
+
+       /*
+        * Don't allow the seek distance to get too large from the
+        * odd fragment, pagein, etc
+        */
+       if (aic->seek_samples <= 60) /* second&third seek */
+               sdist = min(sdist, (aic->seek_mean * 4) + 2*1024*1024);
+       else
+               sdist = min(sdist, (aic->seek_mean * 4) + 2*1024*64);
+
+       aic->seek_samples = (7*aic->seek_samples + 256) / 8;
+       aic->seek_total = (7*aic->seek_total + (u64)256*sdist) / 8;
+       total = aic->seek_total + (aic->seek_samples/2);
+       do_div(total, aic->seek_samples);
+       aic->seek_mean = (sector_t)total;
+}
+
+/*
+ * as_update_iohist keeps a decaying histogram of IO thinktimes, and
+ * updates @aic->ttime_mean based on that. It is called when a new
+ * request is queued.
+ */
+static void as_update_iohist(struct as_data *ad, struct as_io_context *aic,
+                               struct request *rq)
+{
+       struct as_rq *arq = RQ_DATA(rq);
+       int data_dir = arq->is_sync;
+       unsigned long thinktime = 0;
+       sector_t seek_dist;
+
+       if (aic == NULL)
+               return;
+
+       if (data_dir == REQ_SYNC) {
+               unsigned long in_flight = atomic_read(&aic->nr_queued)
+                                       + atomic_read(&aic->nr_dispatched);
+               spin_lock(&aic->lock);
+               if (test_bit(AS_TASK_IORUNNING, &aic->state) ||
+                       test_bit(AS_TASK_IOSTARTED, &aic->state)) {
+                       /* Calculate read -> read thinktime */
+                       if (test_bit(AS_TASK_IORUNNING, &aic->state)
+                                                       && in_flight == 0) {
+                               thinktime = jiffies - aic->last_end_request;
+                               thinktime = min(thinktime, MAX_THINKTIME-1);
+                       }
+                       as_update_thinktime(ad, aic, thinktime);
+
+                       /* 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;
+                       as_update_seekdist(ad, aic, seek_dist);
+               }
+               aic->last_request_pos = rq->sector + rq->nr_sectors;
+               set_bit(AS_TASK_IOSTARTED, &aic->state);
+               spin_unlock(&aic->lock);
+       }
+}
+
 /*
  * as_close_req decides if one request is considered "close" to the
  * previous one issued.
  */
-static int as_close_req(struct as_data *ad, struct as_rq *arq)
+static int as_close_req(struct as_data *ad, struct as_io_context *aic,
+                               struct as_rq *arq)
 {
        unsigned long delay;    /* milliseconds */
        sector_t last = ad->last_sector[ad->batch_data_dir];
        sector_t next = arq->request->sector;
        sector_t delta; /* acceptable close offset (in sectors) */
+       sector_t s;
 
        if (ad->antic_status == ANTIC_OFF || !ad->ioc_finished)
                delay = 0;
        else
                delay = ((jiffies - ad->antic_start) * 1000) / HZ;
 
-       if (delay <= 1)
-               delta = 64;
+       if (delay == 0)
+               delta = 8192;
        else if (delay <= 20 && delay <= ad->antic_expire)
-               delta = 64 << (delay-1);
+               delta = 8192 << delay;
        else
                return 1;
 
-       return (last - (delta>>1) <= next) && (next <= last + delta);
+       if ((last <= next + (delta>>1)) && (next <= last + delta))
+               return 1;
+
+       if (last < next)
+               s = next - last;
+       else
+               s = last - next;
+
+       if (aic->seek_samples == 0) {
+               /*
+                * Process has just started IO. Use past statistics to
+                * gauge success possibility
+                */
+               if (ad->new_seek_mean > s) {
+                       /* this request is better than what we're expecting */
+                       return 1;
+               }
+
+       } else {
+               if (aic->seek_mean > s) {
+                       /* this request is better than what we're expecting */
+                       return 1;
+               }
+       }
+
+       return 0;
 }
 
 /*
@@ -678,7 +643,7 @@ static int as_close_req(struct as_data *ad, struct as_rq *arq)
  * dispatch it ASAP, because we know that application will not be submitting
  * any new reads.
  *
- * If the task which has submitted the request has exitted, break anticipation.
+ * If the task which has submitted the request has exited, break anticipation.
  *
  * If this task has queued some other IO, do not enter enticipation.
  */
@@ -686,7 +651,6 @@ static int as_can_break_anticipation(struct as_data *ad, struct as_rq *arq)
 {
        struct io_context *ioc;
        struct as_io_context *aic;
-       sector_t s;
 
        ioc = ad->io_context;
        BUG_ON(!ioc);
@@ -708,13 +672,6 @@ static int as_can_break_anticipation(struct as_data *ad, struct as_rq *arq)
        if (!aic)
                return 0;
 
-       if (!test_bit(AS_TASK_RUNNING, &aic->state)) {
-               /* process anticipated on has exitted */
-               if (aic->ttime_samples == 0)
-                       ad->exit_prob = (7*ad->exit_prob + 256)/8;
-               return 1;
-       }
-
        if (atomic_read(&aic->nr_queued) > 0) {
                /* process has more requests queued */
                return 1;
@@ -725,62 +682,50 @@ static int as_can_break_anticipation(struct as_data *ad, struct as_rq *arq)
                return 1;
        }
 
-       if (arq && arq->is_sync == REQ_SYNC && as_close_req(ad, arq)) {
+       if (arq && arq->is_sync == REQ_SYNC && as_close_req(ad, aic, arq)) {
                /*
                 * Found a close request that is not one of ours.
                 *
-                * This makes close requests from another process reset
-                * our thinktime delay. Is generally useful when there are
+                * This makes close requests from another process update
+                * our IO history. Is generally useful when there are
                 * two or more cooperating processes working in the same
                 * area.
                 */
-               spin_lock(&aic->lock);
-               aic->last_end_request = jiffies;
-               spin_unlock(&aic->lock);
+               if (!test_bit(AS_TASK_RUNNING, &aic->state)) {
+                       if (aic->ttime_samples == 0)
+                               ad->exit_prob = (7*ad->exit_prob + 256)/8;
+
+                       ad->exit_no_coop = (7*ad->exit_no_coop)/8;
+               }
+
+               as_update_iohist(ad, aic, arq->request);
                return 1;
        }
 
+       if (!test_bit(AS_TASK_RUNNING, &aic->state)) {
+               /* process anticipated on has exited */
+               if (aic->ttime_samples == 0)
+                       ad->exit_prob = (7*ad->exit_prob + 256)/8;
+
+               if (ad->exit_no_coop > 128)
+                       return 1;
+       }
 
        if (aic->ttime_samples == 0) {
                if (ad->new_ttime_mean > ad->antic_expire)
                        return 1;
-               if (ad->exit_prob > 128)
+               if (ad->exit_prob * ad->exit_no_coop > 128*256)
                        return 1;
        } else if (aic->ttime_mean > ad->antic_expire) {
                /* the process thinks too much between requests */
                return 1;
        }
 
-       if (!arq)
-               return 0;
-
-       if (ad->last_sector[REQ_SYNC] < arq->request->sector)
-               s = arq->request->sector - ad->last_sector[REQ_SYNC];
-       else
-               s = ad->last_sector[REQ_SYNC] - arq->request->sector;
-
-       if (aic->seek_samples == 0) {
-               /*
-                * Process has just started IO. Use past statistics to
-                * guage success possibility
-                */
-               if (ad->new_seek_mean > s) {
-                       /* this request is better than what we're expecting */
-                       return 1;
-               }
-
-       } else {
-               if (aic->seek_mean > s) {
-                       /* this request is better than what we're expecting */
-                       return 1;
-               }
-       }
-
        return 0;
 }
 
 /*
- * as_can_anticipate indicates weather we should either run arq
+ * as_can_anticipate indicates whether we should either run arq
  * or keep anticipating a better request.
  */
 static int as_can_anticipate(struct as_data *ad, struct as_rq *arq)
@@ -809,94 +754,11 @@ static int as_can_anticipate(struct as_data *ad, struct as_rq *arq)
         * Status is either ANTIC_OFF so start waiting,
         * ANTIC_WAIT_REQ so continue waiting for request to finish
         * or ANTIC_WAIT_NEXT so continue waiting for an acceptable request.
-        *
         */
 
        return 1;
 }
 
-static void as_update_thinktime(struct as_data *ad, struct as_io_context *aic, unsigned long ttime)
-{
-       /* fixed point: 1.0 == 1<<8 */
-       if (aic->ttime_samples == 0) {
-               ad->new_ttime_total = (7*ad->new_ttime_total + 256*ttime) / 8;
-               ad->new_ttime_mean = ad->new_ttime_total / 256;
-
-               ad->exit_prob = (7*ad->exit_prob)/8;
-       }
-       aic->ttime_samples = (7*aic->ttime_samples + 256) / 8;
-       aic->ttime_total = (7*aic->ttime_total + 256*ttime) / 8;
-       aic->ttime_mean = (aic->ttime_total + 128) / aic->ttime_samples;
-}
-
-static void as_update_seekdist(struct as_data *ad, struct as_io_context *aic, sector_t sdist)
-{
-       u64 total;
-
-       if (aic->seek_samples == 0) {
-               ad->new_seek_total = (7*ad->new_seek_total + 256*(u64)sdist)/8;
-               ad->new_seek_mean = ad->new_seek_total / 256;
-       }
-
-       /*
-        * Don't allow the seek distance to get too large from the
-        * odd fragment, pagein, etc
-        */
-       if (aic->seek_samples <= 60) /* second&third seek */
-               sdist = min(sdist, (aic->seek_mean * 4) + 2*1024*1024);
-       else
-               sdist = min(sdist, (aic->seek_mean * 4) + 2*1024*64);
-
-       aic->seek_samples = (7*aic->seek_samples + 256) / 8;
-       aic->seek_total = (7*aic->seek_total + (u64)256*sdist) / 8;
-       total = aic->seek_total + (aic->seek_samples/2);
-       do_div(total, aic->seek_samples);
-       aic->seek_mean = (sector_t)total;
-}
-
-/*
- * as_update_iohist keeps a decaying histogram of IO thinktimes, and
- * updates @aic->ttime_mean based on that. It is called when a new
- * request is queued.
- */
-static void as_update_iohist(struct as_data *ad, struct as_io_context *aic, struct request *rq)
-{
-       struct as_rq *arq = RQ_DATA(rq);
-       int data_dir = arq->is_sync;
-       unsigned long thinktime;
-       sector_t seek_dist;
-
-       if (aic == NULL)
-               return;
-
-       if (data_dir == REQ_SYNC) {
-               unsigned long in_flight = atomic_read(&aic->nr_queued)
-                                       + atomic_read(&aic->nr_dispatched);
-               spin_lock(&aic->lock);
-               if (test_bit(AS_TASK_IORUNNING, &aic->state) ||
-                       test_bit(AS_TASK_IOSTARTED, &aic->state)) {
-                       /* Calculate read -> read thinktime */
-                       if (test_bit(AS_TASK_IORUNNING, &aic->state)
-                                                       && in_flight == 0) {
-                               thinktime = jiffies - aic->last_end_request;
-                               thinktime = min(thinktime, MAX_THINKTIME-1);
-                       } else
-                               thinktime = 0;
-                       as_update_thinktime(ad, aic, thinktime);
-
-                       /* 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;
-                       as_update_seekdist(ad, aic, seek_dist);
-               }
-               aic->last_request_pos = rq->sector + rq->nr_sectors;
-               set_bit(AS_TASK_IOSTARTED, &aic->state);
-               spin_unlock(&aic->lock);
-       }
-}
-
 /*
  * as_update_arq must be called whenever a request (arq) is added to
  * the sort_list. This function keeps caches up to date, and checks if the
@@ -1032,8 +894,7 @@ static void as_remove_queued_request(request_queue_t *q, struct request *rq)
                ad->next_arq[data_dir] = as_find_next_arq(ad, arq);
 
        list_del_init(&arq->fifo);
-       as_del_arq_hash(arq);
-       as_del_arq_rb(ad, arq);
+       as_del_arq_rb(ad, rq);
 }
 
 /*
@@ -1090,7 +951,7 @@ static void as_move_to_dispatch(struct as_data *ad, struct as_rq *arq)
        struct request *rq = arq->request;
        const int data_dir = arq->is_sync;
 
-       BUG_ON(!ON_RB(&arq->rb_node));
+       BUG_ON(RB_EMPTY_NODE(&rq->rb_node));
 
        as_antic_stop(ad);
        ad->antic_status = ANTIC_OFF;
@@ -1120,23 +981,6 @@ static void as_move_to_dispatch(struct as_data *ad, struct as_rq *arq)
        /*
         * take it off the sort and fifo list, add to dispatch queue
         */
-       while (!list_empty(&rq->queuelist)) {
-               struct request *__rq = list_entry_rq(rq->queuelist.next);
-               struct as_rq *__arq = RQ_DATA(__rq);
-
-               list_del(&__rq->queuelist);
-
-               elv_dispatch_add_tail(ad->q, __rq);
-
-               if (__arq->io_context && __arq->io_context->aic)
-                       atomic_inc(&__arq->io_context->aic->nr_dispatched);
-
-               WARN_ON(__arq->state != AS_RQ_QUEUED);
-               __arq->state = AS_RQ_DISPATCHED;
-
-               ad->nr_dispatched++;
-       }
-
        as_remove_queued_request(ad->q, rq);
        WARN_ON(arq->state != AS_RQ_QUEUED);
 
@@ -1201,7 +1045,7 @@ static int as_dispatch_request(request_queue_t *q, int force)
                || ad->changed_batch)
                return 0;
 
-       if (!(reads && writes && as_batch_expired(ad)) ) {
+       if (!(reads && writes && as_batch_expired(ad))) {
                /*
                 * batch is still running or no reads or no writes
                 */
@@ -1232,7 +1076,7 @@ static int as_dispatch_request(request_queue_t *q, int force)
         */
 
        if (reads) {
-               BUG_ON(RB_EMPTY(&ad->sort_list[REQ_SYNC]));
+               BUG_ON(RB_EMPTY_ROOT(&ad->sort_list[REQ_SYNC]));
 
                if (writes && ad->batch_data_dir == REQ_SYNC)
                        /*
@@ -1256,7 +1100,7 @@ static int as_dispatch_request(request_queue_t *q, int force)
 
        if (writes) {
 dispatch_writes:
-               BUG_ON(RB_EMPTY(&ad->sort_list[REQ_ASYNC]));
+               BUG_ON(RB_EMPTY_ROOT(&ad->sort_list[REQ_ASYNC]));
 
                if (ad->batch_data_dir == REQ_SYNC) {
                        ad->changed_batch = 1;
@@ -1313,58 +1157,18 @@ fifo_expired:
 }
 
 /*
- * Add arq to a list behind alias
- */
-static inline void
-as_add_aliased_request(struct as_data *ad, struct as_rq *arq, struct as_rq *alias)
-{
-       struct request  *req = arq->request;
-       struct list_head *insert = alias->request->queuelist.prev;
-
-       /*
-        * Transfer list of aliases
-        */
-       while (!list_empty(&req->queuelist)) {
-               struct request *__rq = list_entry_rq(req->queuelist.next);
-               struct as_rq *__arq = RQ_DATA(__rq);
-
-               list_move_tail(&__rq->queuelist, &alias->request->queuelist);
-
-               WARN_ON(__arq->state != AS_RQ_QUEUED);
-       }
-
-       /*
-        * Another request with the same start sector on the rbtree.
-        * Link this request to that sector. They are untangled in
-        * as_move_to_dispatch
-        */
-       list_add(&arq->request->queuelist, insert);
-
-       /*
-        * Don't want to have to handle merges.
-        */
-       as_del_arq_hash(arq);
-       arq->request->flags |= REQ_NOMERGE;
-}
-
-/*
  * add arq to rbtree and fifo
  */
 static void as_add_request(request_queue_t *q, struct request *rq)
 {
        struct as_data *ad = q->elevator->elevator_data;
        struct as_rq *arq = RQ_DATA(rq);
-       struct as_rq *alias;
        int data_dir;
 
-       if (arq->state != AS_RQ_PRESCHED) {
-               printk("arq->state: %d\n", arq->state);
-               WARN_ON(1);
-       }
        arq->state = AS_RQ_NEW;
 
        if (rq_data_dir(arq->request) == READ
-                       || current->flags&PF_SYNCWRITE)
+                       || (arq->request->cmd_flags & REQ_RW_SYNC))
                arq->is_sync = 1;
        else
                arq->is_sync = 0;
@@ -1377,33 +1181,15 @@ static void as_add_request(request_queue_t *q, struct request *rq)
                atomic_inc(&arq->io_context->aic->nr_queued);
        }
 
-       alias = as_add_arq_rb(ad, arq);
-       if (!alias) {
-               /*
-                * set expire time (only used for reads) and add to fifo list
-                */
-               arq->expires = jiffies + ad->fifo_expire[data_dir];
-               list_add_tail(&arq->fifo, &ad->fifo_list[data_dir]);
-
-               if (rq_mergeable(arq->request))
-                       as_add_arq_hash(ad, arq);
-               as_update_arq(ad, arq); /* keep state machine up to date */
-
-       } else {
-               as_add_aliased_request(ad, arq, alias);
+       as_add_arq_rb(ad, rq);
 
-               /*
-                * have we been anticipating this request?
-                * or does it come from the same process as the one we are
-                * anticipating for?
-                */
-               if (ad->antic_status == ANTIC_WAIT_REQ
-                               || ad->antic_status == ANTIC_WAIT_NEXT) {
-                       if (as_can_break_anticipation(ad, arq))
-                               as_antic_stop(ad);
-               }
-       }
+       /*
+        * set expire time (only used for reads) and add to fifo list
+        */
+       arq->expires = jiffies + ad->fifo_expire[data_dir];
+       list_add_tail(&arq->fifo, &ad->fifo_list[data_dir]);
 
+       as_update_arq(ad, arq); /* keep state machine up to date */
        arq->state = AS_RQ_QUEUED;
 }
 
@@ -1441,108 +1227,35 @@ static int as_queue_empty(request_queue_t *q)
                && list_empty(&ad->fifo_list[REQ_SYNC]);
 }
 
-static struct request *
-as_former_request(request_queue_t *q, struct request *rq)
-{
-       struct as_rq *arq = RQ_DATA(rq);
-       struct rb_node *rbprev = rb_prev(&arq->rb_node);
-       struct request *ret = NULL;
-
-       if (rbprev)
-               ret = rb_entry_arq(rbprev)->request;
-
-       return ret;
-}
-
-static struct request *
-as_latter_request(request_queue_t *q, struct request *rq)
-{
-       struct as_rq *arq = RQ_DATA(rq);
-       struct rb_node *rbnext = rb_next(&arq->rb_node);
-       struct request *ret = NULL;
-
-       if (rbnext)
-               ret = rb_entry_arq(rbnext)->request;
-
-       return ret;
-}
-
 static int
 as_merge(request_queue_t *q, struct request **req, struct bio *bio)
 {
        struct as_data *ad = q->elevator->elevator_data;
        sector_t rb_key = bio->bi_sector + bio_sectors(bio);
        struct request *__rq;
-       int ret;
-
-       /*
-        * see if the merge hash can satisfy a back merge
-        */
-       __rq = as_find_arq_hash(ad, bio->bi_sector);
-       if (__rq) {
-               BUG_ON(__rq->sector + __rq->nr_sectors != bio->bi_sector);
-
-               if (elv_rq_merge_ok(__rq, bio)) {
-                       ret = ELEVATOR_BACK_MERGE;
-                       goto out;
-               }
-       }
 
        /*
         * check for front merge
         */
-       __rq = as_find_arq_rb(ad, rb_key, bio_data_dir(bio));
-       if (__rq) {
-               BUG_ON(rb_key != rq_rb_key(__rq));
-
-               if (elv_rq_merge_ok(__rq, bio)) {
-                       ret = ELEVATOR_FRONT_MERGE;
-                       goto out;
-               }
+       __rq = elv_rb_find(&ad->sort_list[bio_data_dir(bio)], rb_key);
+       if (__rq && elv_rq_merge_ok(__rq, bio)) {
+               *req = __rq;
+               return ELEVATOR_FRONT_MERGE;
        }
 
        return ELEVATOR_NO_MERGE;
-out:
-       if (ret) {
-               if (rq_mergeable(__rq))
-                       as_hot_arq_hash(ad, RQ_DATA(__rq));
-       }
-       *req = __rq;
-       return ret;
 }
 
-static void as_merged_request(request_queue_t *q, struct request *req)
+static void as_merged_request(request_queue_t *q, struct request *req, int type)
 {
        struct as_data *ad = q->elevator->elevator_data;
-       struct as_rq *arq = RQ_DATA(req);
-
-       /*
-        * hash always needs to be repositioned, key is end sector
-        */
-       as_del_arq_hash(arq);
-       as_add_arq_hash(ad, arq);
 
        /*
         * if the merge was a front merge, we need to reposition request
         */
-       if (rq_rb_key(req) != arq->rb_key) {
-               struct as_rq *alias, *next_arq = NULL;
-
-               if (ad->next_arq[arq->is_sync] == arq)
-                       next_arq = as_find_next_arq(ad, arq);
-
-               /*
-                * Note! We should really be moving any old aliased requests
-                * off this request and try to insert them into the rbtree. We
-                * currently don't bother. Ditto the next function.
-                */
-               as_del_arq_rb(ad, arq);
-               if ((alias = as_add_arq_rb(ad, arq)) ) {
-                       list_del_init(&arq->fifo);
-                       as_add_aliased_request(ad, arq, alias);
-                       if (next_arq)
-                               ad->next_arq[arq->is_sync] = next_arq;
-               }
+       if (type == ELEVATOR_FRONT_MERGE) {
+               as_del_arq_rb(ad, req);
+               as_add_arq_rb(ad, req);
                /*
                 * Note! At this stage of this and the next function, our next
                 * request may not be optimal - eg the request may have "grown"
@@ -1551,11 +1264,9 @@ static void as_merged_request(request_queue_t *q, struct request *req)
        }
 }
 
-static void
-as_merged_requests(request_queue_t *q, struct request *req,
-                        struct request *next)
+static void as_merged_requests(request_queue_t *q, struct request *req,
+                               struct request *next)
 {
-       struct as_data *ad = q->elevator->elevator_data;
        struct as_rq *arq = RQ_DATA(req);
        struct as_rq *anext = RQ_DATA(next);
 
@@ -1563,28 +1274,6 @@ as_merged_requests(request_queue_t *q, struct request *req,
        BUG_ON(!anext);
 
        /*
-        * reposition arq (this is the merged request) in hash, and in rbtree
-        * in case of a front merge
-        */
-       as_del_arq_hash(arq);
-       as_add_arq_hash(ad, arq);
-
-       if (rq_rb_key(req) != arq->rb_key) {
-               struct as_rq *alias, *next_arq = NULL;
-
-               if (ad->next_arq[arq->is_sync] == arq)
-                       next_arq = as_find_next_arq(ad, arq);
-
-               as_del_arq_rb(ad, arq);
-               if ((alias = as_add_arq_rb(ad, arq)) ) {
-                       list_del_init(&arq->fifo);
-                       as_add_aliased_request(ad, arq, alias);
-                       if (next_arq)
-                               ad->next_arq[arq->is_sync] = next_arq;
-               }
-       }
-
-       /*
         * if anext expires before arq, assign its expire time to arq
         * and move into anext position (anext will be deleted) in fifo
         */
@@ -1601,18 +1290,6 @@ as_merged_requests(request_queue_t *q, struct request *req,
        }
 
        /*
-        * Transfer list of aliases
-        */
-       while (!list_empty(&next->queuelist)) {
-               struct request *__rq = list_entry_rq(next->queuelist.next);
-               struct as_rq *__arq = RQ_DATA(__rq);
-
-               list_move_tail(&__rq->queuelist, &req->queuelist);
-
-               WARN_ON(__arq->state != AS_RQ_QUEUED);
-       }
-
-       /*
         * kill knowledge of next, this one is a goner
         */
        as_remove_queued_request(q, next);
@@ -1670,12 +1347,9 @@ static int as_set_request(request_queue_t *q, struct request *rq,
 
        if (arq) {
                memset(arq, 0, sizeof(*arq));
-               RB_CLEAR(&arq->rb_node);
                arq->request = rq;
                arq->state = AS_RQ_PRESCHED;
                arq->io_context = NULL;
-               INIT_LIST_HEAD(&arq->hash);
-               arq->on_hash = 0;
                INIT_LIST_HEAD(&arq->fifo);
                rq->elevator_private = arq;
                return 0;
@@ -1712,7 +1386,6 @@ static void as_exit_queue(elevator_t *e)
 
        mempool_destroy(ad->arq_pool);
        put_io_context(ad->io_context);
-       kfree(ad->hash);
        kfree(ad);
 }
 
@@ -1720,34 +1393,25 @@ static void as_exit_queue(elevator_t *e)
  * initialize elevator private data (as_data), and alloc a arq for
  * each request on the free lists
  */
-static int as_init_queue(request_queue_t *q, elevator_t *e)
+static void *as_init_queue(request_queue_t *q, elevator_t *e)
 {
        struct as_data *ad;
-       int i;
 
        if (!arq_pool)
-               return -ENOMEM;
+               return NULL;
 
        ad = kmalloc_node(sizeof(*ad), GFP_KERNEL, q->node);
        if (!ad)
-               return -ENOMEM;
+               return NULL;
        memset(ad, 0, sizeof(*ad));
 
        ad->q = q; /* Identify what queue the data belongs to */
 
-       ad->hash = kmalloc_node(sizeof(struct list_head)*AS_HASH_ENTRIES,
-                               GFP_KERNEL, q->node);
-       if (!ad->hash) {
-               kfree(ad);
-               return -ENOMEM;
-       }
-
        ad->arq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab,
                                mempool_free_slab, arq_pool, q->node);
        if (!ad->arq_pool) {
-               kfree(ad->hash);
                kfree(ad);
-               return -ENOMEM;
+               return NULL;
        }
 
        /* anticipatory scheduling helpers */
@@ -1756,9 +1420,6 @@ static int as_init_queue(request_queue_t *q, elevator_t *e)
        init_timer(&ad->antic_timer);
        INIT_WORK(&ad->antic_work, as_work_handler, q);
 
-       for (i = 0; i < AS_HASH_ENTRIES; i++)
-               INIT_LIST_HEAD(&ad->hash[i]);
-
        INIT_LIST_HEAD(&ad->fifo_list[REQ_SYNC]);
        INIT_LIST_HEAD(&ad->fifo_list[REQ_ASYNC]);
        ad->sort_list[REQ_SYNC] = RB_ROOT;
@@ -1768,24 +1429,18 @@ static int as_init_queue(request_queue_t *q, elevator_t *e)
        ad->antic_expire = default_antic_expire;
        ad->batch_expire[REQ_SYNC] = default_read_batch_expire;
        ad->batch_expire[REQ_ASYNC] = default_write_batch_expire;
-       e->elevator_data = ad;
 
        ad->current_batch_expires = jiffies + ad->batch_expire[REQ_SYNC];
        ad->write_batch_count = ad->batch_expire[REQ_ASYNC] / 10;
        if (ad->write_batch_count < 2)
                ad->write_batch_count = 2;
 
-       return 0;
+       return ad;
 }
 
 /*
  * sysfs parts below
  */
-struct as_fs_entry {
-       struct attribute attr;
-       ssize_t (*show)(struct as_data *, char *);
-       ssize_t (*store)(struct as_data *, const char *, size_t);
-};
 
 static ssize_t
 as_var_show(unsigned int var, char *page)
@@ -1802,33 +1457,41 @@ as_var_store(unsigned long *var, const char *page, size_t count)
        return count;
 }
 
-static ssize_t as_est_show(struct as_data *ad, char *page)
+static ssize_t est_time_show(elevator_t *e, char *page)
 {
+       struct as_data *ad = e->elevator_data;
        int pos = 0;
 
-       pos += sprintf(page+pos, "%lu %% exit probability\n", 100*ad->exit_prob/256);
+       pos += sprintf(page+pos, "%lu %% exit probability\n",
+                               100*ad->exit_prob/256);
+       pos += sprintf(page+pos, "%lu %% probability of exiting without a "
+                               "cooperating process submitting IO\n",
+                               100*ad->exit_no_coop/256);
        pos += sprintf(page+pos, "%lu ms new thinktime\n", ad->new_ttime_mean);
-       pos += sprintf(page+pos, "%llu sectors new seek distance\n", (unsigned long long)ad->new_seek_mean);
+       pos += sprintf(page+pos, "%llu sectors new seek distance\n",
+                               (unsigned long long)ad->new_seek_mean);
 
        return pos;
 }
 
 #define SHOW_FUNCTION(__FUNC, __VAR)                           \
-static ssize_t __FUNC(struct as_data *ad, char *page)          \
+static ssize_t __FUNC(elevator_t *e, char *page)               \
 {                                                              \
+       struct as_data *ad = e->elevator_data;                  \
        return as_var_show(jiffies_to_msecs((__VAR)), (page));  \
 }
-SHOW_FUNCTION(as_readexpire_show, ad->fifo_expire[REQ_SYNC]);
-SHOW_FUNCTION(as_writeexpire_show, ad->fifo_expire[REQ_ASYNC]);
-SHOW_FUNCTION(as_anticexpire_show, ad->antic_expire);
-SHOW_FUNCTION(as_read_batchexpire_show, ad->batch_expire[REQ_SYNC]);
-SHOW_FUNCTION(as_write_batchexpire_show, ad->batch_expire[REQ_ASYNC]);
+SHOW_FUNCTION(as_read_expire_show, ad->fifo_expire[REQ_SYNC]);
+SHOW_FUNCTION(as_write_expire_show, ad->fifo_expire[REQ_ASYNC]);
+SHOW_FUNCTION(as_antic_expire_show, ad->antic_expire);
+SHOW_FUNCTION(as_read_batch_expire_show, ad->batch_expire[REQ_SYNC]);
+SHOW_FUNCTION(as_write_batch_expire_show, ad->batch_expire[REQ_ASYNC]);
 #undef SHOW_FUNCTION
 
 #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX)                                \
-static ssize_t __FUNC(struct as_data *ad, const char *page, size_t count)      \
+static ssize_t __FUNC(elevator_t *e, const char *page, size_t count)   \
 {                                                                      \
-       int ret = as_var_store(__PTR, (page), count);           \
+       struct as_data *ad = e->elevator_data;                          \
+       int ret = as_var_store(__PTR, (page), count);                   \
        if (*(__PTR) < (MIN))                                           \
                *(__PTR) = (MIN);                                       \
        else if (*(__PTR) > (MAX))                                      \
@@ -1836,90 +1499,26 @@ static ssize_t __FUNC(struct as_data *ad, const char *page, size_t count)       \
        *(__PTR) = msecs_to_jiffies(*(__PTR));                          \
        return ret;                                                     \
 }
-STORE_FUNCTION(as_readexpire_store, &ad->fifo_expire[REQ_SYNC], 0, INT_MAX);
-STORE_FUNCTION(as_writeexpire_store, &ad->fifo_expire[REQ_ASYNC], 0, INT_MAX);
-STORE_FUNCTION(as_anticexpire_store, &ad->antic_expire, 0, INT_MAX);
-STORE_FUNCTION(as_read_batchexpire_store,
+STORE_FUNCTION(as_read_expire_store, &ad->fifo_expire[REQ_SYNC], 0, INT_MAX);
+STORE_FUNCTION(as_write_expire_store, &ad->fifo_expire[REQ_ASYNC], 0, INT_MAX);
+STORE_FUNCTION(as_antic_expire_store, &ad->antic_expire, 0, INT_MAX);
+STORE_FUNCTION(as_read_batch_expire_store,
                        &ad->batch_expire[REQ_SYNC], 0, INT_MAX);
-STORE_FUNCTION(as_write_batchexpire_store,
+STORE_FUNCTION(as_write_batch_expire_store,
                        &ad->batch_expire[REQ_ASYNC], 0, INT_MAX);
 #undef STORE_FUNCTION
 
-static struct as_fs_entry as_est_entry = {
-       .attr = {.name = "est_time", .mode = S_IRUGO },
-       .show = as_est_show,
-};
-static struct as_fs_entry as_readexpire_entry = {
-       .attr = {.name = "read_expire", .mode = S_IRUGO | S_IWUSR },
-       .show = as_readexpire_show,
-       .store = as_readexpire_store,
-};
-static struct as_fs_entry as_writeexpire_entry = {
-       .attr = {.name = "write_expire", .mode = S_IRUGO | S_IWUSR },
-       .show = as_writeexpire_show,
-       .store = as_writeexpire_store,
-};
-static struct as_fs_entry as_anticexpire_entry = {
-       .attr = {.name = "antic_expire", .mode = S_IRUGO | S_IWUSR },
-       .show = as_anticexpire_show,
-       .store = as_anticexpire_store,
-};
-static struct as_fs_entry as_read_batchexpire_entry = {
-       .attr = {.name = "read_batch_expire", .mode = S_IRUGO | S_IWUSR },
-       .show = as_read_batchexpire_show,
-       .store = as_read_batchexpire_store,
-};
-static struct as_fs_entry as_write_batchexpire_entry = {
-       .attr = {.name = "write_batch_expire", .mode = S_IRUGO | S_IWUSR },
-       .show = as_write_batchexpire_show,
-       .store = as_write_batchexpire_store,
-};
-
-static struct attribute *default_attrs[] = {
-       &as_est_entry.attr,
-       &as_readexpire_entry.attr,
-       &as_writeexpire_entry.attr,
-       &as_anticexpire_entry.attr,
-       &as_read_batchexpire_entry.attr,
-       &as_write_batchexpire_entry.attr,
-       NULL,
-};
-
-#define to_as(atr) container_of((atr), struct as_fs_entry, attr)
-
-static ssize_t
-as_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
-{
-       elevator_t *e = container_of(kobj, elevator_t, kobj);
-       struct as_fs_entry *entry = to_as(attr);
-
-       if (!entry->show)
-               return -EIO;
-
-       return entry->show(e->elevator_data, page);
-}
-
-static ssize_t
-as_attr_store(struct kobject *kobj, struct attribute *attr,
-                   const char *page, size_t length)
-{
-       elevator_t *e = container_of(kobj, elevator_t, kobj);
-       struct as_fs_entry *entry = to_as(attr);
-
-       if (!entry->store)
-               return -EIO;
-
-       return entry->store(e->elevator_data, page, length);
-}
-
-static struct sysfs_ops as_sysfs_ops = {
-       .show   = as_attr_show,
-       .store  = as_attr_store,
-};
-
-static struct kobj_type as_ktype = {
-       .sysfs_ops      = &as_sysfs_ops,
-       .default_attrs  = default_attrs,
+#define AS_ATTR(name) \
+       __ATTR(name, S_IRUGO|S_IWUSR, as_##name##_show, as_##name##_store)
+
+static struct elv_fs_entry as_attrs[] = {
+       __ATTR_RO(est_time),
+       AS_ATTR(read_expire),
+       AS_ATTR(write_expire),
+       AS_ATTR(antic_expire),
+       AS_ATTR(read_batch_expire),
+       AS_ATTR(write_batch_expire),
+       __ATTR_NULL
 };
 
 static struct elevator_type iosched_as = {
@@ -1933,16 +1532,17 @@ static struct elevator_type iosched_as = {
                .elevator_deactivate_req_fn =   as_deactivate_request,
                .elevator_queue_empty_fn =      as_queue_empty,
                .elevator_completed_req_fn =    as_completed_request,
-               .elevator_former_req_fn =       as_former_request,
-               .elevator_latter_req_fn =       as_latter_request,
+               .elevator_former_req_fn =       elv_rb_former_request,
+               .elevator_latter_req_fn =       elv_rb_latter_request,
                .elevator_set_req_fn =          as_set_request,
                .elevator_put_req_fn =          as_put_request,
                .elevator_may_queue_fn =        as_may_queue,
                .elevator_init_fn =             as_init_queue,
                .elevator_exit_fn =             as_exit_queue,
+               .trim =                         as_trim,
        },
 
-       .elevator_ktype = &as_ktype,
+       .elevator_attrs = as_attrs,
        .elevator_name = "anticipatory",
        .elevator_owner = THIS_MODULE,
 };
@@ -1973,7 +1573,14 @@ static int __init as_init(void)
 
 static void __exit as_exit(void)
 {
+       DECLARE_COMPLETION(all_gone);
        elv_unregister(&iosched_as);
+       ioc_gone = &all_gone;
+       /* ioc_gone's update must be visible before reading ioc_count */
+       smp_wmb();
+       if (atomic_read(&ioc_count))
+               wait_for_completion(ioc_gone);
+       synchronize_rcu();
        kmem_cache_destroy(arq_pool);
 }