Patch from Nick Piggin Contributions from Mitchell Blank Jr 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 #include +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, _