]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] fix AS hangs
authorNick Piggin <piggin@cyberone.com.au>
Fri, 26 Sep 2003 00:55:08 +0000 (17:55 -0700)
committerLinus Torvalds <torvalds@home.osdl.org>
Fri, 26 Sep 2003 00:55:08 +0000 (17:55 -0700)
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

index 6ef1db60da84a2322b67de16bcd1bdde3aed9629..dec9ad1d59acaefddf89921fed0a5162b4709265 100644 (file)
@@ -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
         */