Commit 5daa5a40 authored by Nick Piggin's avatar Nick Piggin Committed by Linus Torvalds

[PATCH] fix AS hangs

Clears the next_arq cache if a request is repositioned and added as an alias
behind another.  Should fix the BUGs which Mary Edie Meredith has been
reporting.

Also, fixes a few random (but harmless) cases where last_merge was being set
and causing warnings.

When 2 requests are merged, and one is thrown away, move its alias list onto
the merged request! This is likely to be the cause of Mary's hangs.
parent 91b8e4de
......@@ -302,7 +302,7 @@ static struct request *as_find_arq_hash(struct as_data *ad, sector_t offset)
BUG_ON(!arq->on_hash);
if (!rq_mergeable(__rq)) {
__as_del_arq_hash(arq);
as_remove_merge_hints(ad->q, arq);
continue;
}
......@@ -1028,9 +1028,15 @@ static void as_remove_request(request_queue_t *q, struct request *rq)
return;
}
if (ON_RB(&arq->rb_node))
if (ON_RB(&arq->rb_node)) {
/*
* We'll lose the aliased request(s) here. I don't think this
* will ever happen, but if it does, hopefully someone will
* report it.
*/
WARN_ON(!list_empty(&rq->queuelist));
as_remove_queued_request(q, rq);
else
} else
as_remove_dispatched_request(q, rq);
}
......@@ -1085,6 +1091,7 @@ static inline int as_batch_expired(struct as_data *ad)
*/
static void as_move_to_dispatch(struct as_data *ad, struct as_rq *arq)
{
struct request *rq = arq->request;
struct list_head *insert;
const int data_dir = arq->is_sync;
......@@ -1097,8 +1104,7 @@ static void as_move_to_dispatch(struct as_data *ad, struct as_rq *arq)
* This has to be set in order to be correctly updated by
* as_find_next_arq
*/
ad->last_sector[data_dir] = arq->request->sector
+ arq->request->nr_sectors;
ad->last_sector[data_dir] = rq->sector + rq->nr_sectors;
if (data_dir == REQ_SYNC) {
/* In case we have to anticipate after this */
......@@ -1119,15 +1125,15 @@ 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
*/
as_remove_queued_request(ad->q, arq->request);
as_remove_queued_request(ad->q, rq);
insert = ad->dispatch->prev;
while (!list_empty(&arq->request->queuelist)) {
struct request *rq = list_entry_rq(arq->request->queuelist.next);
struct as_rq *__arq = RQ_DATA(rq);
while (!list_empty(&rq->queuelist)) {
struct request *__rq = list_entry_rq(rq->queuelist.next);
struct as_rq *__arq = RQ_DATA(__rq);
list_move_tail(&rq->queuelist, ad->dispatch);
list_move_tail(&__rq->queuelist, ad->dispatch);
if (__arq->io_context && __arq->io_context->aic)
atomic_inc(&__arq->io_context->aic->nr_dispatched);
......@@ -1138,7 +1144,7 @@ static void as_move_to_dispatch(struct as_data *ad, struct as_rq *arq)
ad->nr_dispatched++;
}
list_add(&arq->request->queuelist, insert);
list_add(&rq->queuelist, insert);
if (arq->io_context && arq->io_context->aic)
atomic_inc(&arq->io_context->aic->nr_dispatched);
......@@ -1146,7 +1152,6 @@ static void as_move_to_dispatch(struct as_data *ad, struct as_rq *arq)
arq->state = AS_RQ_DISPATCHED;
ad->nr_dispatched++;
}
/*
......@@ -1310,13 +1315,12 @@ as_add_aliased_request(struct as_data *ad, struct as_rq *arq, struct as_rq *alia
* Link this request to that sector. They are untangled in
* as_move_to_dispatch
*/
list_add_tail(&arq->request->queuelist, &alias->request->queuelist);
list_add_tail(&arq->request->queuelist, &alias->request->queuelist);
/*
* Don't want to have to handle merges.
*/
as_remove_merge_hints(ad->q, arq);
}
/*
......@@ -1433,7 +1437,7 @@ as_insert_request(request_queue_t *q, struct request *rq, int where)
as_add_request(ad, arq);
break;
default:
printk("%s: bad insert point %d\n", __FUNCTION__,where);
BUG();
return;
}
}
......@@ -1527,10 +1531,13 @@ as_merge(request_queue_t *q, struct request **req, struct bio *bio)
return ELEVATOR_NO_MERGE;
out:
q->last_merge = __rq;
if (rq_mergeable(__rq))
q->last_merge = __rq;
out_insert:
if (ret)
as_hot_arq_hash(ad, RQ_DATA(__rq));
if (ret) {
if (rq_mergeable(__rq))
as_hot_arq_hash(ad, RQ_DATA(__rq));
}
*req = __rq;
return ret;
}
......@@ -1550,7 +1557,10 @@ static void as_merged_request(request_queue_t *q, struct request *req)
* if the merge was a front merge, we need to reposition request
*/
if (rq_rb_key(req) != arq->rb_key) {
struct as_rq *alias;
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
......@@ -1561,6 +1571,8 @@ static void as_merged_request(request_queue_t *q, struct request *req)
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;
}
/*
* Note! At this stage of this and the next function, our next
......@@ -1591,11 +1603,17 @@ as_merged_requests(request_queue_t *q, struct request *req,
as_add_arq_hash(ad, arq);
if (rq_rb_key(req) != arq->rb_key) {
struct as_rq *alias;
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;
}
}
......@@ -1615,6 +1633,18 @@ 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
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment