From 5daa5a408467246821cb6be3152cf0e0915bd0b8 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Thu, 25 Sep 2003 17:55:08 -0700 Subject: [PATCH] [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. --- drivers/block/as-iosched.c | 70 +++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/drivers/block/as-iosched.c b/drivers/block/as-iosched.c index 6ef1db60da84a..dec9ad1d59aca 100644 --- a/drivers/block/as-iosched.c +++ b/drivers/block/as-iosched.c @@ -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 */ -- 2.53.0