10. The runtime data of Deadline i/o scheduler block/deadline-iosched.c
struct deadline_data {
/* requests (deadline_rq s) are present on both sort_list and fifo_list */
struct rb_root sort_list[2];
struct list_head fifo_list[2];
/* next in sort order. read, write or both are NULL */
struct request *next_rq[2];
unsigned int batching; /* number of sequential requests made */
sector_t last_sector; /* head position */
unsigned int starved; /* times reads have starved writes */
/* settings that change how the i/o scheduler behaves */
int fifo_expire[2];
int fifo_batch;
int writes_starved;
int front_merges;
};
struct list_head fifo_list[READ] 6 4 5
struct rb_root sort_list[READ]
6 5 4 next_rq[READ]
next_rq[WRITE]
struct rb_root sort_list[WRITE] 9 8 7
struct list_head fifo_list[WRITE] 10
7 8 9
11. block/deadline-iosched.c
Add a request to both rb tree and fifo list
static void
deadline_add_request(struct request_queue *q, struct request *rq)
{
struct deadline_data *dd = q->elevator->elevator_data;
const int data_dir = rq_data_dir(rq);
deadline_add_rq_rb(dd, rq);
/*
* set expire time (only used for reads) and add to fifo list
*/
rq_set_fifo_time(rq, jiffies + dd->fifo_expire[data_dir]);
list_add_tail(&rq->queuelist, &dd->fifo_list[data_dir]);
}
struct list_head fifo_list[READ] 6 4 5
struct rb_root sort_list[READ]
6 5 4 next_rq[READ]
next_rq[WRITE]
struct rb_root sort_list[WRITE] 9 8 7
struct list_head fifo_list[WRITE] 11
7 8 9
12. struct list_head fifo_list[READ] 6 4 5 block/deadline-iosched.c
struct rb_root sort_list[READ]
6 5 4 next_rq[READ]
next_rq[WRITE]
struct rb_root sort_list[WRITE] 9 8 7
struct list_head fifo_list[WRITE]
7 8 9
1.Check if we are running a sequential batch, and it is still entitled.
if (dd->next_rq[WRITE])
rq = dd->next_rq[WRITE];
else
rq = dd->next_rq[READ];
if (rq) {
/* we have a "next request" */
if (dd->last_sector != rq->sector)
/* end the batch on a non sequential request */
dd->batching += dd->fifo_batch;
if (dd->batching < dd->fifo_batch)
/* we are still entitled to batch */
goto dispatch_request;
} 12
13. struct list_head fifo_list[READ] 6 4 5 block/deadline-iosched.c
struct rb_root sort_list[READ]
6 5 4
struct rb_root sort_list[WRITE] 9 8 7
struct list_head fifo_list[WRITE]
7 8 9
2. If we are not running a batch. Choose a new direction to serve requests.
A read request is always favored, unless write has been starved.
if (reads) {
if (writes && (dd->starved++ >= dd->writes_starved))
goto dispatch_writes;
data_dir = READ;
goto dispatch_find_request;
}
if (writes) {
dispatch_writes:
dd->starved = 0;
data_dir = WRITE;
goto dispatch_find_request;
}
13
14. struct list_head fifo_list[READ] 6 4 5 block/deadline-iosched.c
struct rb_root sort_list[READ]
6 5 4 next_rq[READ]
struct rb_root sort_list[READ]
6 5 4 next_rq[READ]
3.Choose an appropriate request..
If the first request of the fifo list has expired, serve it.
Otherwise, behave as an “One-way Elevator”
dispatch_find_request:
if (deadline_check_fifo(dd, data_dir)) {
dd->batching = 0;
rq = rq_entry_fifo(dd->fifo_list[data_dir].next);
} else if (dd->next_rq[data_dir]) {
rq = dd->next_rq[data_dir];
} else {
struct rb_node *node;
dd->batching = 0;
node = rb_first(&dd->sort_list[data_dir]);
if (node)
rq = rb_entry_rq(node);
} 14
15. block/deadline-iosched.c
Dispatch the request, remove it from the elevator’s private queue
and put it in the dispatch queue.
Also update the information about the “last” and the “next” request.
struct list_head fifo_list[READ] 6 4 5
4
struct rb_root sort_list[READ]
6 5
Suppose the request 4 was picked in the previous step. next_rq[READ]
static void
deadline_move_request(struct deadline_data *dd, struct request *rq)
{
const int data_dir = rq_data_dir(rq);
struct rb_node *rbnext = rb_next(&rq->rb_node); Disk
dd->next_rq[READ] = NULL;
dd->next_rq[WRITE] = NULL;
if (rbnext)
dd->next_rq[data_dir] = rb_entry_rq(rbnext);
dd->last_sector = rq->sector + rq->nr_sectors;
deadline_move_to_dispatch(dd, rq);
} 15
16. block/as-iosched.c
static void as_add_request(request_queue_t *q, struct request *rq)
{
struct as_data *ad = q->elevator->elevator_data;
int data_dir;
RQ_SET_STATE(rq, AS_RQ_NEW);
data_dir = rq_is_sync(rq);
rq->elevator_private = as_get_io_context(q->node);
if (RQ_IOC(rq)) {
as_update_iohist(ad, RQ_IOC(rq)->aic, rq);
atomic_inc(&RQ_IOC(rq)->aic->nr_queued);
}
as_add_rq_rb(ad, rq);
/*
* set expire time (only used for reads) and add to fifo list
*/
rq_set_fifo_time(rq, jiffies + ad->fifo_expire[data_dir]);
list_add_tail(&rq->queuelist, &ad->fifo_list[data_dir]);
as_update_rq(ad, rq); /* keep state machine up to date */
RQ_SET_STATE(rq, AS_RQ_QUEUED);
}
16
21. enum anticipation_status {
ANTIC_OFF = 0, /* Not anticipating (normal operation) */
ANTIC_WAIT_REQ, /* The last read has not yet completed */
ANTIC_WAIT_NEXT, /* Currently anticipating a request vs
last read (which has completed) */
ANTIC_FINISHED, /* Anticipating but have found a candidate or timed out */
};
as_add_request() as_dispatch_request() as_completed_request()
as_add_rq_rb()
as_update_rq()
as_move_to_dispatch() as_antic_waitreq()
as_antic_stop() as_antic_timeout() as_antic_waitnext()
ANTIC_FINISHED ANTIC_OFF ANTIC_WAIT_REQ ANTIC_WAIT_NEXT
21
kblockd_schedule_work()
22. /*
* This is called directly by the functions in this file to stop anticipation.
* We kill the timer and schedule a call to the request_fn asap.
*/
static void as_antic_stop(struct as_data *ad)
{
int status = ad->antic_status;
if (status == ANTIC_WAIT_REQ || status == ANTIC_WAIT_NEXT) {
if (status == ANTIC_WAIT_NEXT)
del_timer(&ad->antic_timer);
ad->antic_status = ANTIC_FINISHED;
/* see as_work_handler */
kblockd_schedule_work(&ad->antic_work);
}
}
22
23. /*
* as_update_rq must be called whenever a request (rq) 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 as_update_rq(struct as_data *ad, struct request *rq)
{
const int data_dir = rq_is_sync(rq);
/* keep the next_rq cache up to date */
ad->next_rq[data_dir] = as_choose_req(ad, rq, ad->next_rq[data_dir]);
/*
* 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, rq))
as_antic_stop(ad);
}
}
23
24. /*
* This is executed in a "deferred" process context, by kblockd. It calls the
* driver's request_fn so the driver can submit that request.
*
* IMPORTANT! This guy will reenter the elevator, so set up all queue global
* state before calling, and don't rely on any state over calls.
*
* FIXME! dispatch queue is not a queue at all!
*/
static void as_work_handler(void *data)
{
struct request_queue *q = data;
unsigned long flags;
spin_lock_irqsave(q->queue_lock, flags);
blk_start_queueing(q);
spin_unlock_irqrestore(q->queue_lock, flags);
}
24
25. /*
* as_antic_timeout is the timer function set by as_antic_waitnext.
*/
static void as_antic_timeout(unsigned long data)
{
struct request_queue *q = (struct request_queue *)data;
struct as_data *ad = q->elevator->elevator_data;
unsigned long flags;
spin_lock_irqsave(q->queue_lock, flags);
if (ad->antic_status == ANTIC_WAIT_REQ
|| ad->antic_status == ANTIC_WAIT_NEXT) {
struct as_io_context *aic = ad->io_context->aic;
ad->antic_status = ANTIC_FINISHED;
kblockd_schedule_work(&ad->antic_work);
if (aic->ttime_samples == 0) {
/* 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);
}
25