/*
- * linux/drivers/block/elevator.c
- *
* Block device elevator/IO-scheduler.
*
* Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
/*
* If the given scheduler is not available, fall back to no-op.
*/
- if (!(e = elevator_find(chosen_elevator)))
+ if ((e = elevator_find(chosen_elevator)))
+ elevator_put(e);
+ else
strcpy(chosen_elevator, "noop");
- elevator_put(e);
}
static int __init elevator_setup(char *str)
eq = kmalloc(sizeof(struct elevator_queue), GFP_KERNEL);
if (!eq) {
- elevator_put(e->elevator_type);
+ elevator_put(e);
return -ENOMEM;
}
ret = elevator_attach(q, e, eq);
if (ret) {
kfree(eq);
- elevator_put(e->elevator_type);
+ elevator_put(e);
}
return ret;
if (q->last_merge == rq)
q->last_merge = NULL;
+ q->nr_sorted--;
boundary = q->end_sector;
if (e->ops->elevator_merge_req_fn)
e->ops->elevator_merge_req_fn(q, rq, next);
+ q->nr_sorted--;
q->last_merge = rq;
}
rq->flags &= ~REQ_STARTED;
- /*
- * if this is the flush, requeue the original instead and drop the flush
- */
- if (rq->flags & REQ_BAR_FLUSH) {
- clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags);
- rq = rq->end_io_data;
- }
+ __elv_add_request(q, rq, ELEVATOR_INSERT_REQUEUE, 0);
+}
- __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0);
+static void elv_drain_elevator(request_queue_t *q)
+{
+ static int printed;
+ while (q->elevator->ops->elevator_dispatch_fn(q, 1))
+ ;
+ if (q->nr_sorted == 0)
+ return;
+ if (printed++ < 10) {
+ printk(KERN_ERR "%s: forced dispatching is broken "
+ "(nr_sorted=%u), please report this\n",
+ q->elevator->elevator_type->elevator_name, q->nr_sorted);
+ }
}
void __elv_add_request(request_queue_t *q, struct request *rq, int where,
int plug)
{
+ struct list_head *pos;
+ unsigned ordseq;
+
+ if (q->ordcolor)
+ rq->flags |= REQ_ORDERED_COLOR;
+
if (rq->flags & (REQ_SOFTBARRIER | REQ_HARDBARRIER)) {
+ /*
+ * toggle ordered color
+ */
+ q->ordcolor ^= 1;
+
/*
* barriers implicitly indicate back insertion
*/
case ELEVATOR_INSERT_BACK:
rq->flags |= REQ_SOFTBARRIER;
-
- while (q->elevator->ops->elevator_dispatch_fn(q, 1))
- ;
+ elv_drain_elevator(q);
list_add_tail(&rq->queuelist, &q->queue_head);
/*
* We kick the queue here for the following reasons.
case ELEVATOR_INSERT_SORT:
BUG_ON(!blk_fs_request(rq));
rq->flags |= REQ_SORTED;
+ q->nr_sorted++;
if (q->last_merge == NULL && rq_mergeable(rq))
q->last_merge = rq;
/*
q->elevator->ops->elevator_add_req_fn(q, rq);
break;
+ case ELEVATOR_INSERT_REQUEUE:
+ /*
+ * If ordered flush isn't in progress, we do front
+ * insertion; otherwise, requests should be requeued
+ * in ordseq order.
+ */
+ rq->flags |= REQ_SOFTBARRIER;
+
+ if (q->ordseq == 0) {
+ list_add(&rq->queuelist, &q->queue_head);
+ break;
+ }
+
+ ordseq = blk_ordered_req_seq(rq);
+
+ list_for_each(pos, &q->queue_head) {
+ struct request *pos_rq = list_entry_rq(pos);
+ if (ordseq <= blk_ordered_req_seq(pos_rq))
+ break;
+ }
+
+ list_add_tail(&rq->queuelist, pos);
+ break;
+
default:
printk(KERN_ERR "%s: bad insertion point %d\n",
__FUNCTION__, where);
{
struct request *rq;
- if (unlikely(list_empty(&q->queue_head) &&
- !q->elevator->ops->elevator_dispatch_fn(q, 0)))
- return NULL;
-
- rq = list_entry_rq(q->queue_head.next);
-
- /*
- * if this is a barrier write and the device has to issue a
- * flush sequence to support it, check how far we are
- */
- if (blk_fs_request(rq) && blk_barrier_rq(rq)) {
- BUG_ON(q->ordered == QUEUE_ORDERED_NONE);
+ while (1) {
+ while (!list_empty(&q->queue_head)) {
+ rq = list_entry_rq(q->queue_head.next);
+ if (blk_do_ordered(q, &rq))
+ return rq;
+ }
- if (q->ordered == QUEUE_ORDERED_FLUSH &&
- !blk_barrier_preflush(rq))
- rq = blk_start_pre_flush(q, rq);
+ if (!q->elevator->ops->elevator_dispatch_fn(q, 0))
+ return NULL;
}
-
- return rq;
}
struct request *elv_next_request(request_queue_t *q)
blkdev_dequeue_request(rq);
rq->flags |= REQ_QUIET;
end_that_request_chunk(rq, 0, nr_bytes);
- end_that_request_last(rq);
+ end_that_request_last(rq, 0);
} else {
printk(KERN_ERR "%s: bad return=%d\n", __FUNCTION__,
ret);
struct request *elv_latter_request(request_queue_t *q, struct request *rq)
{
- struct list_head *next;
-
elevator_t *e = q->elevator;
if (e->ops->elevator_latter_req_fn)
return e->ops->elevator_latter_req_fn(q, rq);
-
- next = rq->queuelist.next;
- if (next != &q->queue_head && next != &rq->queuelist)
- return list_entry_rq(next);
-
return NULL;
}
struct request *elv_former_request(request_queue_t *q, struct request *rq)
{
- struct list_head *prev;
-
elevator_t *e = q->elevator;
if (e->ops->elevator_former_req_fn)
return e->ops->elevator_former_req_fn(q, rq);
-
- prev = rq->queuelist.prev;
- if (prev != &q->queue_head && prev != &rq->queuelist)
- return list_entry_rq(prev);
-
return NULL;
}
* request is released from the driver, io must be done
*/
if (blk_account_rq(rq)) {
+ struct request *first_rq = list_entry_rq(q->queue_head.next);
+
q->in_flight--;
+
+ /*
+ * Check if the queue is waiting for fs requests to be
+ * drained for flush sequence.
+ */
+ if (q->ordseq && q->in_flight == 0 &&
+ blk_ordered_cur_seq(q) == QUEUE_ORDSEQ_DRAIN &&
+ blk_ordered_req_seq(first_rq) > QUEUE_ORDSEQ_DRAIN) {
+ blk_ordered_complete_seq(q, QUEUE_ORDSEQ_DRAIN, 0);
+ q->request_fn(q);
+ }
+
if (blk_sorted_rq(rq) && e->ops->elevator_completed_req_fn)
e->ops->elevator_completed_req_fn(q, rq);
}
set_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags);
- while (q->elevator->ops->elevator_dispatch_fn(q, 1))
- ;
+ elv_drain_elevator(q);
while (q->rq.elvpriv) {
+ blk_remove_plug(q);
+ q->request_fn(q);
spin_unlock_irq(q->queue_lock);
msleep(10);
spin_lock_irq(q->queue_lock);
+ elv_drain_elevator(q);
}
spin_unlock_irq(q->queue_lock);
ssize_t elv_iosched_store(request_queue_t *q, const char *name, size_t count)
{
char elevator_name[ELV_NAME_MAX];
+ size_t len;
struct elevator_type *e;
- memset(elevator_name, 0, sizeof(elevator_name));
- strncpy(elevator_name, name, sizeof(elevator_name));
+ elevator_name[sizeof(elevator_name) - 1] = '\0';
+ strncpy(elevator_name, name, sizeof(elevator_name) - 1);
+ len = strlen(elevator_name);
- if (elevator_name[strlen(elevator_name) - 1] == '\n')
- elevator_name[strlen(elevator_name) - 1] = '\0';
+ if (len && elevator_name[len - 1] == '\n')
+ elevator_name[len - 1] = '\0';
e = elevator_get(elevator_name);
if (!e) {