From 2824bc9328467127083c1325f54b67d298c333b2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 20 Oct 2005 10:56:41 +0200 Subject: [PATCH] fix try_module_get race in elevator_find This patch removes try_module_get race in elevator_find. try_module_get should always be called with the spinlock protecting what the module init/cleanup routines register/unregister to held. In the case of elevators, we should be holding elv_list to avoid it going away between spin_unlock_irq and try_module_get. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- drivers/block/elevator.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'drivers/block/elevator.c') diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 98f0126a2deb..4144f30d82a9 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -97,7 +97,6 @@ static struct elevator_type *elevator_find(const char *name) struct elevator_type *e = NULL; struct list_head *entry; - spin_lock_irq(&elv_list_lock); list_for_each(entry, &elv_list) { struct elevator_type *__e; @@ -108,7 +107,6 @@ static struct elevator_type *elevator_find(const char *name) break; } } - spin_unlock_irq(&elv_list_lock); return e; } @@ -120,12 +118,15 @@ static void elevator_put(struct elevator_type *e) static struct elevator_type *elevator_get(const char *name) { - struct elevator_type *e = elevator_find(name); + struct elevator_type *e; - if (!e) - return NULL; - if (!try_module_get(e->elevator_owner)) - return NULL; + spin_lock_irq(&elv_list_lock); + + e = elevator_find(name); + if (e && !try_module_get(e->elevator_owner)) + e = NULL; + + spin_unlock_irq(&elv_list_lock); return e; } @@ -153,11 +154,15 @@ static char chosen_elevator[16]; static void elevator_setup_default(void) { + struct elevator_type *e; + /* * check if default is set and exists */ - if (chosen_elevator[0] && elevator_find(chosen_elevator)) + if (chosen_elevator[0] && (e = elevator_get(chosen_elevator))) { + elevator_put(e); return; + } #if defined(CONFIG_IOSCHED_AS) strcpy(chosen_elevator, "anticipatory"); @@ -555,10 +560,9 @@ void elv_unregister_queue(struct request_queue *q) int elv_register(struct elevator_type *e) { + spin_lock_irq(&elv_list_lock); if (elevator_find(e->elevator_name)) BUG(); - - spin_lock_irq(&elv_list_lock); list_add_tail(&e->list, &elv_list); spin_unlock_irq(&elv_list_lock); -- cgit v1.2.2 From 8922e16cf6269e668123acb1ae1fdc62b7a3a4fc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 20 Oct 2005 16:23:44 +0200 Subject: [PATCH] 01/05 Implement generic dispatch queue Implements generic dispatch queue which can replace all dispatch queues implemented by each iosched. This reduces code duplication, eases enforcing semantics over dispatch queue, and simplifies specific ioscheds. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- drivers/block/elevator.c | 242 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 170 insertions(+), 72 deletions(-) (limited to 'drivers/block/elevator.c') diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 4144f30d82a9..a27555908d35 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -40,6 +40,11 @@ static DEFINE_SPINLOCK(elv_list_lock); static LIST_HEAD(elv_list); +static inline sector_t rq_last_sector(struct request *rq) +{ + return rq->sector + rq->nr_sectors; +} + /* * can we safely merge with this request? */ @@ -143,6 +148,9 @@ static int elevator_attach(request_queue_t *q, struct elevator_type *e, INIT_LIST_HEAD(&q->queue_head); q->last_merge = NULL; q->elevator = eq; + q->last_sector = 0; + q->boundary_rq = NULL; + q->max_back_kb = 0; if (eq->ops->elevator_init_fn) ret = eq->ops->elevator_init_fn(q, eq); @@ -225,6 +233,48 @@ void elevator_exit(elevator_t *e) kfree(e); } +/* + * Insert rq into dispatch queue of q. Queue lock must be held on + * entry. If sort != 0, rq is sort-inserted; otherwise, rq will be + * appended to the dispatch queue. To be used by specific elevators. + */ +void elv_dispatch_insert(request_queue_t *q, struct request *rq, int sort) +{ + sector_t boundary; + unsigned max_back; + struct list_head *entry; + + if (!sort) { + /* Specific elevator is performing sort. Step away. */ + q->last_sector = rq_last_sector(rq); + q->boundary_rq = rq; + list_add_tail(&rq->queuelist, &q->queue_head); + return; + } + + boundary = q->last_sector; + max_back = q->max_back_kb * 2; + boundary = boundary > max_back ? boundary - max_back : 0; + + list_for_each_prev(entry, &q->queue_head) { + struct request *pos = list_entry_rq(entry); + + if (pos->flags & (REQ_SOFTBARRIER|REQ_HARDBARRIER|REQ_STARTED)) + break; + if (rq->sector >= boundary) { + if (pos->sector < boundary) + continue; + } else { + if (pos->sector >= boundary) + break; + } + if (rq->sector >= pos->sector) + break; + } + + list_add(&rq->queuelist, entry); +} + int elv_merge(request_queue_t *q, struct request **req, struct bio *bio) { elevator_t *e = q->elevator; @@ -255,13 +305,7 @@ void elv_merge_requests(request_queue_t *q, struct request *rq, e->ops->elevator_merge_req_fn(q, rq, next); } -/* - * For careful internal use by the block layer. Essentially the same as - * a requeue in that it tells the io scheduler that this request is not - * active in the driver or hardware anymore, but we don't want the request - * added back to the scheduler. Function is not exported. - */ -void elv_deactivate_request(request_queue_t *q, struct request *rq) +void elv_requeue_request(request_queue_t *q, struct request *rq) { elevator_t *e = q->elevator; @@ -269,19 +313,14 @@ void elv_deactivate_request(request_queue_t *q, struct request *rq) * it already went through dequeue, we need to decrement the * in_flight count again */ - if (blk_account_rq(rq)) + if (blk_account_rq(rq)) { q->in_flight--; + if (blk_sorted_rq(rq) && e->ops->elevator_deactivate_req_fn) + e->ops->elevator_deactivate_req_fn(q, rq); + } rq->flags &= ~REQ_STARTED; - if (e->ops->elevator_deactivate_req_fn) - e->ops->elevator_deactivate_req_fn(q, rq); -} - -void elv_requeue_request(request_queue_t *q, struct request *rq) -{ - elv_deactivate_request(q, rq); - /* * if this is the flush, requeue the original instead and drop the flush */ @@ -290,55 +329,89 @@ void elv_requeue_request(request_queue_t *q, struct request *rq) rq = rq->end_io_data; } - /* - * the request is prepped and may have some resources allocated. - * allowing unprepped requests to pass this one may cause resource - * deadlock. turn on softbarrier. - */ - rq->flags |= REQ_SOFTBARRIER; - - /* - * if iosched has an explicit requeue hook, then use that. otherwise - * just put the request at the front of the queue - */ - if (q->elevator->ops->elevator_requeue_req_fn) - q->elevator->ops->elevator_requeue_req_fn(q, rq); - else - __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0); + __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0); } void __elv_add_request(request_queue_t *q, struct request *rq, int where, int plug) { - /* - * barriers implicitly indicate back insertion - */ - if (rq->flags & (REQ_SOFTBARRIER | REQ_HARDBARRIER) && - where == ELEVATOR_INSERT_SORT) - where = ELEVATOR_INSERT_BACK; + if (rq->flags & (REQ_SOFTBARRIER | REQ_HARDBARRIER)) { + /* + * barriers implicitly indicate back insertion + */ + if (where == ELEVATOR_INSERT_SORT) + where = ELEVATOR_INSERT_BACK; + + /* + * this request is scheduling boundary, update last_sector + */ + if (blk_fs_request(rq)) { + q->last_sector = rq_last_sector(rq); + q->boundary_rq = rq; + } + } if (plug) blk_plug_device(q); rq->q = q; - if (!test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags)) { - q->elevator->ops->elevator_add_req_fn(q, rq, where); - - if (blk_queue_plugged(q)) { - int nrq = q->rq.count[READ] + q->rq.count[WRITE] - - q->in_flight; - - if (nrq >= q->unplug_thresh) - __generic_unplug_device(q); - } - } else + if (unlikely(test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags))) { /* * if drain is set, store the request "locally". when the drain * is finished, the requests will be handed ordered to the io * scheduler */ list_add_tail(&rq->queuelist, &q->drain_list); + return; + } + + switch (where) { + case ELEVATOR_INSERT_FRONT: + rq->flags |= REQ_SOFTBARRIER; + + list_add(&rq->queuelist, &q->queue_head); + break; + + case ELEVATOR_INSERT_BACK: + rq->flags |= REQ_SOFTBARRIER; + + while (q->elevator->ops->elevator_dispatch_fn(q, 1)) + ; + list_add_tail(&rq->queuelist, &q->queue_head); + /* + * We kick the queue here for the following reasons. + * - The elevator might have returned NULL previously + * to delay requests and returned them now. As the + * queue wasn't empty before this request, ll_rw_blk + * won't run the queue on return, resulting in hang. + * - Usually, back inserted requests won't be merged + * with anything. There's no point in delaying queue + * processing. + */ + blk_remove_plug(q); + q->request_fn(q); + break; + + case ELEVATOR_INSERT_SORT: + BUG_ON(!blk_fs_request(rq)); + rq->flags |= REQ_SORTED; + q->elevator->ops->elevator_add_req_fn(q, rq); + break; + + default: + printk(KERN_ERR "%s: bad insertion point %d\n", + __FUNCTION__, where); + BUG(); + } + + if (blk_queue_plugged(q)) { + int nrq = q->rq.count[READ] + q->rq.count[WRITE] + - q->in_flight; + + if (nrq >= q->unplug_thresh) + __generic_unplug_device(q); + } } void elv_add_request(request_queue_t *q, struct request *rq, int where, @@ -353,13 +426,19 @@ void elv_add_request(request_queue_t *q, struct request *rq, int where, static inline struct request *__elv_next_request(request_queue_t *q) { - struct request *rq = q->elevator->ops->elevator_next_req_fn(q); + 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 (rq && blk_fs_request(rq) && blk_barrier_rq(rq)) { + if (blk_fs_request(rq) && blk_barrier_rq(rq)) { BUG_ON(q->ordered == QUEUE_ORDERED_NONE); if (q->ordered == QUEUE_ORDERED_FLUSH && @@ -376,16 +455,34 @@ struct request *elv_next_request(request_queue_t *q) int ret; while ((rq = __elv_next_request(q)) != NULL) { - /* - * just mark as started even if we don't start it, a request - * that has been delayed should not be passed by new incoming - * requests - */ - rq->flags |= REQ_STARTED; + if (!(rq->flags & REQ_STARTED)) { + elevator_t *e = q->elevator; + + /* + * This is the first time the device driver + * sees this request (possibly after + * requeueing). Notify IO scheduler. + */ + if (blk_sorted_rq(rq) && + e->ops->elevator_activate_req_fn) + e->ops->elevator_activate_req_fn(q, rq); + + /* + * just mark as started even if we don't start + * it, a request that has been delayed should + * not be passed by new incoming requests + */ + rq->flags |= REQ_STARTED; + } if (rq == q->last_merge) q->last_merge = NULL; + if (!q->boundary_rq || q->boundary_rq == rq) { + q->last_sector = rq_last_sector(rq); + q->boundary_rq = NULL; + } + if ((rq->flags & REQ_DONTPREP) || !q->prep_rq_fn) break; @@ -396,9 +493,9 @@ struct request *elv_next_request(request_queue_t *q) /* * the request may have been (partially) prepped. * we need to keep this request in the front to - * avoid resource deadlock. turn on softbarrier. + * avoid resource deadlock. REQ_STARTED will + * prevent other fs requests from passing this one. */ - rq->flags |= REQ_SOFTBARRIER; rq = NULL; break; } else if (ret == BLKPREP_KILL) { @@ -421,16 +518,16 @@ struct request *elv_next_request(request_queue_t *q) return rq; } -void elv_remove_request(request_queue_t *q, struct request *rq) +void elv_dequeue_request(request_queue_t *q, struct request *rq) { - elevator_t *e = q->elevator; + BUG_ON(list_empty(&rq->queuelist)); + + list_del_init(&rq->queuelist); /* * the time frame between a request being removed from the lists * and to it is freed is accounted as io that is in progress at - * the driver side. note that we only account requests that the - * driver has seen (REQ_STARTED set), to avoid false accounting - * for request-request merges + * the driver side. */ if (blk_account_rq(rq)) q->in_flight++; @@ -444,19 +541,19 @@ void elv_remove_request(request_queue_t *q, struct request *rq) */ if (rq == q->last_merge) q->last_merge = NULL; - - if (e->ops->elevator_remove_req_fn) - e->ops->elevator_remove_req_fn(q, rq); } int elv_queue_empty(request_queue_t *q) { elevator_t *e = q->elevator; + if (!list_empty(&q->queue_head)) + return 0; + if (e->ops->elevator_queue_empty_fn) return e->ops->elevator_queue_empty_fn(q); - return list_empty(&q->queue_head); + return 1; } struct request *elv_latter_request(request_queue_t *q, struct request *rq) @@ -528,11 +625,11 @@ void elv_completed_request(request_queue_t *q, struct request *rq) /* * request is released from the driver, io must be done */ - if (blk_account_rq(rq)) + if (blk_account_rq(rq)) { q->in_flight--; - - if (e->ops->elevator_completed_req_fn) - e->ops->elevator_completed_req_fn(q, rq); + if (blk_sorted_rq(rq) && e->ops->elevator_completed_req_fn) + e->ops->elevator_completed_req_fn(q, rq); + } } int elv_register_queue(struct request_queue *q) @@ -705,11 +802,12 @@ ssize_t elv_iosched_show(request_queue_t *q, char *name) return len; } +EXPORT_SYMBOL(elv_dispatch_insert); EXPORT_SYMBOL(elv_add_request); EXPORT_SYMBOL(__elv_add_request); EXPORT_SYMBOL(elv_requeue_request); EXPORT_SYMBOL(elv_next_request); -EXPORT_SYMBOL(elv_remove_request); +EXPORT_SYMBOL(elv_dequeue_request); EXPORT_SYMBOL(elv_queue_empty); EXPORT_SYMBOL(elv_completed_request); EXPORT_SYMBOL(elevator_exit); -- cgit v1.2.2 From 1b47f531e244e339b648dfff071c086f936e49e1 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 20 Oct 2005 16:37:00 +0200 Subject: [PATCH] generic dispatch fixes - Split elv_dispatch_insert() into two functions - Rename rq_last_sector() to rq_end_sector() Signed-off-by: Jens Axboe --- drivers/block/elevator.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) (limited to 'drivers/block/elevator.c') diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index a27555908d35..237e43d760c8 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -40,11 +40,6 @@ static DEFINE_SPINLOCK(elv_list_lock); static LIST_HEAD(elv_list); -static inline sector_t rq_last_sector(struct request *rq) -{ - return rq->sector + rq->nr_sectors; -} - /* * can we safely merge with this request? */ @@ -148,7 +143,7 @@ static int elevator_attach(request_queue_t *q, struct elevator_type *e, INIT_LIST_HEAD(&q->queue_head); q->last_merge = NULL; q->elevator = eq; - q->last_sector = 0; + q->end_sector = 0; q->boundary_rq = NULL; q->max_back_kb = 0; @@ -233,29 +228,25 @@ void elevator_exit(elevator_t *e) kfree(e); } +void elv_dispatch_insert_tail(request_queue_t *q, struct request *rq) +{ +} + /* * Insert rq into dispatch queue of q. Queue lock must be held on * entry. If sort != 0, rq is sort-inserted; otherwise, rq will be * appended to the dispatch queue. To be used by specific elevators. */ -void elv_dispatch_insert(request_queue_t *q, struct request *rq, int sort) +void elv_dispatch_sort(request_queue_t *q, struct request *rq) { sector_t boundary; unsigned max_back; struct list_head *entry; - if (!sort) { - /* Specific elevator is performing sort. Step away. */ - q->last_sector = rq_last_sector(rq); - q->boundary_rq = rq; - list_add_tail(&rq->queuelist, &q->queue_head); - return; - } - - boundary = q->last_sector; + boundary = q->end_sector; max_back = q->max_back_kb * 2; boundary = boundary > max_back ? boundary - max_back : 0; - + list_for_each_prev(entry, &q->queue_head) { struct request *pos = list_entry_rq(entry); @@ -343,10 +334,10 @@ void __elv_add_request(request_queue_t *q, struct request *rq, int where, where = ELEVATOR_INSERT_BACK; /* - * this request is scheduling boundary, update last_sector + * this request is scheduling boundary, update end_sector */ if (blk_fs_request(rq)) { - q->last_sector = rq_last_sector(rq); + q->end_sector = rq_end_sector(rq); q->boundary_rq = rq; } } @@ -479,7 +470,7 @@ struct request *elv_next_request(request_queue_t *q) q->last_merge = NULL; if (!q->boundary_rq || q->boundary_rq == rq) { - q->last_sector = rq_last_sector(rq); + q->end_sector = rq_end_sector(rq); q->boundary_rq = NULL; } @@ -802,7 +793,7 @@ ssize_t elv_iosched_show(request_queue_t *q, char *name) return len; } -EXPORT_SYMBOL(elv_dispatch_insert); +EXPORT_SYMBOL(elv_dispatch_sort); EXPORT_SYMBOL(elv_add_request); EXPORT_SYMBOL(__elv_add_request); EXPORT_SYMBOL(elv_requeue_request); -- cgit v1.2.2 From d9ebb192aa13a026edc6faff137dcb14f2c91731 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 20 Oct 2005 16:37:38 +0200 Subject: [PATCH] elevator: leftover function declaration Signed-off-by: Jens Axboe --- drivers/block/elevator.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/block/elevator.c') diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 237e43d760c8..29d6c8237ab3 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -228,10 +228,6 @@ void elevator_exit(elevator_t *e) kfree(e); } -void elv_dispatch_insert_tail(request_queue_t *q, struct request *rq) -{ -} - /* * Insert rq into dispatch queue of q. Queue lock must be held on * entry. If sort != 0, rq is sort-inserted; otherwise, rq will be -- cgit v1.2.2 From 06b86245c052963029bfd9020ca1f08ceb66f85a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 20 Oct 2005 16:46:23 +0200 Subject: [PATCH] 03/05 move last_merge handlin into generic elevator code Currently, both generic elevator code and specific ioscheds participate in the management and usage of last_merge. This and the following patches move last_merge handling into generic elevator code. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- drivers/block/elevator.c | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) (limited to 'drivers/block/elevator.c') diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 29d6c8237ab3..415144372c75 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -83,15 +83,6 @@ inline int elv_try_merge(struct request *__rq, struct bio *bio) } EXPORT_SYMBOL(elv_try_merge); -inline int elv_try_last_merge(request_queue_t *q, struct bio *bio) -{ - if (q->last_merge) - return elv_try_merge(q->last_merge, bio); - - return ELEVATOR_NO_MERGE; -} -EXPORT_SYMBOL(elv_try_last_merge); - static struct elevator_type *elevator_find(const char *name) { struct elevator_type *e = NULL; @@ -239,6 +230,9 @@ void elv_dispatch_sort(request_queue_t *q, struct request *rq) unsigned max_back; struct list_head *entry; + if (q->last_merge == rq) + q->last_merge = NULL; + boundary = q->end_sector; max_back = q->max_back_kb * 2; boundary = boundary > max_back ? boundary - max_back : 0; @@ -265,6 +259,15 @@ void elv_dispatch_sort(request_queue_t *q, struct request *rq) int elv_merge(request_queue_t *q, struct request **req, struct bio *bio) { elevator_t *e = q->elevator; + int ret; + + if (q->last_merge) { + ret = elv_try_merge(q->last_merge, bio); + if (ret != ELEVATOR_NO_MERGE) { + *req = q->last_merge; + return ret; + } + } if (e->ops->elevator_merge_fn) return e->ops->elevator_merge_fn(q, req, bio); @@ -278,6 +281,8 @@ void elv_merged_request(request_queue_t *q, struct request *rq) if (e->ops->elevator_merged_fn) e->ops->elevator_merged_fn(q, rq); + + q->last_merge = rq; } void elv_merge_requests(request_queue_t *q, struct request *rq, @@ -285,11 +290,10 @@ void elv_merge_requests(request_queue_t *q, struct request *rq, { elevator_t *e = q->elevator; - if (q->last_merge == next) - q->last_merge = NULL; - if (e->ops->elevator_merge_req_fn) e->ops->elevator_merge_req_fn(q, rq, next); + + q->last_merge = rq; } void elv_requeue_request(request_queue_t *q, struct request *rq) @@ -384,6 +388,8 @@ void __elv_add_request(request_queue_t *q, struct request *rq, int where, BUG_ON(!blk_fs_request(rq)); rq->flags |= REQ_SORTED; q->elevator->ops->elevator_add_req_fn(q, rq); + if (q->last_merge == NULL && rq_mergeable(rq)) + q->last_merge = rq; break; default: @@ -462,9 +468,6 @@ struct request *elv_next_request(request_queue_t *q) rq->flags |= REQ_STARTED; } - if (rq == q->last_merge) - q->last_merge = NULL; - if (!q->boundary_rq || q->boundary_rq == rq) { q->end_sector = rq_end_sector(rq); q->boundary_rq = NULL; @@ -518,16 +521,6 @@ void elv_dequeue_request(request_queue_t *q, struct request *rq) */ if (blk_account_rq(rq)) q->in_flight++; - - /* - * the main clearing point for q->last_merge is on retrieval of - * request by driver (it calls elv_next_request()), but it _can_ - * also happen here if a request is added to the queue but later - * deleted without ever being given to driver (merged with another - * request). - */ - if (rq == q->last_merge) - q->last_merge = NULL; } int elv_queue_empty(request_queue_t *q) -- cgit v1.2.2 From cb19833dccb32f97cacbfff834b53523915f13f6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 24 Oct 2005 08:35:58 +0200 Subject: [BLOCK] kill generic max_back_kb handling This patch kills max_back_kb handling from elv_dispatch_sort() and kills max_back_kb field from struct request_queue. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- drivers/block/elevator.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/block/elevator.c') diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 415144372c75..af2388e73f61 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -136,7 +136,6 @@ static int elevator_attach(request_queue_t *q, struct elevator_type *e, q->elevator = eq; q->end_sector = 0; q->boundary_rq = NULL; - q->max_back_kb = 0; if (eq->ops->elevator_init_fn) ret = eq->ops->elevator_init_fn(q, eq); @@ -227,16 +226,13 @@ void elevator_exit(elevator_t *e) void elv_dispatch_sort(request_queue_t *q, struct request *rq) { sector_t boundary; - unsigned max_back; struct list_head *entry; if (q->last_merge == rq) q->last_merge = NULL; boundary = q->end_sector; - max_back = q->max_back_kb * 2; - boundary = boundary > max_back ? boundary - max_back : 0; - + list_for_each_prev(entry, &q->queue_head) { struct request *pos = list_entry_rq(entry); -- cgit v1.2.2 From cb98fc8bb9c141009e2bda99c0db39d387e142cf Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 28 Oct 2005 08:29:39 +0200 Subject: [BLOCK] Reimplement elevator switch This patch reimplements elevator switch. This patch assumes generic dispatch queue patchset is applied. * Each request is tagged with REQ_ELVPRIV flag if it has its elevator private data set. * Requests which doesn't have REQ_ELVPRIV flag set never enter iosched. They are always directly back inserted to dispatch queue. Of course, elevator_put_req_fn is called only for requests which have its REQ_ELVPRIV set. * Request queue maintains the current number of requests which have its elevator data set (elevator_set_req_fn called) in q->rq->elvpriv. * If a request queue has QUEUE_FLAG_BYPASS set, elevator private data is not allocated for new requests. To switch to another iosched, we set QUEUE_FLAG_BYPASS and wait until elvpriv goes to zero; then, we attach the new iosched and clears QUEUE_FLAG_BYPASS. New implementation is much simpler and main code paths are less cluttered, IMHO. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- drivers/block/elevator.c | 78 ++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 43 deletions(-) (limited to 'drivers/block/elevator.c') diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index af2388e73f61..272d93946621 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -131,11 +132,7 @@ static int elevator_attach(request_queue_t *q, struct elevator_type *e, eq->ops = &e->ops; eq->elevator_type = e; - INIT_LIST_HEAD(&q->queue_head); - q->last_merge = NULL; q->elevator = eq; - q->end_sector = 0; - q->boundary_rq = NULL; if (eq->ops->elevator_init_fn) ret = eq->ops->elevator_init_fn(q, eq); @@ -184,6 +181,12 @@ int elevator_init(request_queue_t *q, char *name) struct elevator_queue *eq; int ret = 0; + INIT_LIST_HEAD(&q->queue_head); + q->last_merge = NULL; + q->end_sector = 0; + q->boundary_rq = NULL; + q->max_back_kb = 0; + elevator_setup_default(); if (!name) @@ -336,23 +339,14 @@ void __elv_add_request(request_queue_t *q, struct request *rq, int where, q->end_sector = rq_end_sector(rq); q->boundary_rq = rq; } - } + } else if (!(rq->flags & REQ_ELVPRIV) && where == ELEVATOR_INSERT_SORT) + where = ELEVATOR_INSERT_BACK; if (plug) blk_plug_device(q); rq->q = q; - if (unlikely(test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags))) { - /* - * if drain is set, store the request "locally". when the drain - * is finished, the requests will be handed ordered to the io - * scheduler - */ - list_add_tail(&rq->queuelist, &q->drain_list); - return; - } - switch (where) { case ELEVATOR_INSERT_FRONT: rq->flags |= REQ_SOFTBARRIER; @@ -659,25 +653,36 @@ EXPORT_SYMBOL_GPL(elv_unregister); * switch to new_e io scheduler. be careful not to introduce deadlocks - * we don't free the old io scheduler, before we have allocated what we * need for the new one. this way we have a chance of going back to the old - * one, if the new one fails init for some reason. we also do an intermediate - * switch to noop to ensure safety with stack-allocated requests, since they - * don't originate from the block layer allocator. noop is safe here, because - * it never needs to touch the elevator itself for completion events. DRAIN - * flags will make sure we don't touch it for additions either. + * one, if the new one fails init for some reason. */ static void elevator_switch(request_queue_t *q, struct elevator_type *new_e) { - elevator_t *e = kmalloc(sizeof(elevator_t), GFP_KERNEL); - struct elevator_type *noop_elevator = NULL; - elevator_t *old_elevator; + elevator_t *old_elevator, *e; + /* + * Allocate new elevator + */ + e = kmalloc(sizeof(elevator_t), GFP_KERNEL); if (!e) goto error; /* - * first step, drain requests from the block freelist + * Turn on BYPASS and drain all requests w/ elevator private data */ - blk_wait_queue_drained(q, 0); + spin_lock_irq(q->queue_lock); + + set_bit(QUEUE_FLAG_BYPASS, &q->queue_flags); + + while (q->elevator->ops->elevator_dispatch_fn(q, 1)) + ; + + while (q->rq.elvpriv) { + spin_unlock_irq(q->queue_lock); + msleep(100); + spin_lock_irq(q->queue_lock); + } + + spin_unlock_irq(q->queue_lock); /* * unregister old elevator data @@ -685,18 +690,6 @@ static void elevator_switch(request_queue_t *q, struct elevator_type *new_e) elv_unregister_queue(q); old_elevator = q->elevator; - /* - * next step, switch to noop since it uses no private rq structures - * and doesn't allocate any memory for anything. then wait for any - * non-fs requests in-flight - */ - noop_elevator = elevator_get("noop"); - spin_lock_irq(q->queue_lock); - elevator_attach(q, noop_elevator, e); - spin_unlock_irq(q->queue_lock); - - blk_wait_queue_drained(q, 1); - /* * attach and start new elevator */ @@ -707,11 +700,10 @@ static void elevator_switch(request_queue_t *q, struct elevator_type *new_e) goto fail_register; /* - * finally exit old elevator and start queue again + * finally exit old elevator and turn off BYPASS. */ elevator_exit(old_elevator); - blk_finish_queue_drain(q); - elevator_put(noop_elevator); + clear_bit(QUEUE_FLAG_BYPASS, &q->queue_flags); return; fail_register: @@ -720,13 +712,13 @@ fail_register: * one again (along with re-adding the sysfs dir) */ elevator_exit(e); + e = NULL; fail: q->elevator = old_elevator; elv_register_queue(q); - blk_finish_queue_drain(q); + clear_bit(QUEUE_FLAG_BYPASS, &q->queue_flags); + kfree(e); error: - if (noop_elevator) - elevator_put(noop_elevator); elevator_put(new_e); printk(KERN_ERR "elevator: switch to %s failed\n",new_e->elevator_name); } -- cgit v1.2.2 From 64521d1a3ba7cc7fe4ab5640da83d88144f05340 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 28 Oct 2005 08:30:39 +0200 Subject: [BLOCK] elevator switch fixes/cleanup - 100msec sleep is a little excessive, lots of requests can complete in that timeframe. Use 10msec instead. - Rename QUEUE_FLAG_BYPASS to QUEUE_FLAG_ELVSWITCH to indicate what is going on. Signed-off-by: Jens Axboe --- drivers/block/elevator.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/block/elevator.c') diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 272d93946621..cf9bedbb761b 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -671,14 +671,14 @@ static void elevator_switch(request_queue_t *q, struct elevator_type *new_e) */ spin_lock_irq(q->queue_lock); - set_bit(QUEUE_FLAG_BYPASS, &q->queue_flags); + set_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); while (q->elevator->ops->elevator_dispatch_fn(q, 1)) ; while (q->rq.elvpriv) { spin_unlock_irq(q->queue_lock); - msleep(100); + msleep(10); spin_lock_irq(q->queue_lock); } @@ -703,7 +703,7 @@ static void elevator_switch(request_queue_t *q, struct elevator_type *new_e) * finally exit old elevator and turn off BYPASS. */ elevator_exit(old_elevator); - clear_bit(QUEUE_FLAG_BYPASS, &q->queue_flags); + clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); return; fail_register: @@ -716,7 +716,7 @@ fail_register: fail: q->elevator = old_elevator; elv_register_queue(q); - clear_bit(QUEUE_FLAG_BYPASS, &q->queue_flags); + clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); kfree(e); error: elevator_put(new_e); -- cgit v1.2.2 From 772eca7825358f52cf208afd7ab8ce22722ce41a Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 28 Oct 2005 09:01:17 +0200 Subject: [BLOCK] Leftover reference to ->max_back_kb Signed-off-by: Jens Axboe --- drivers/block/elevator.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/block/elevator.c') diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index cf9bedbb761b..8de77416bd2f 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -185,7 +185,6 @@ int elevator_init(request_queue_t *q, char *name) q->last_merge = NULL; q->end_sector = 0; q->boundary_rq = NULL; - q->max_back_kb = 0; elevator_setup_default(); -- cgit v1.2.2 From 8267e268e0914ac9371d07f711fcf20cc572993c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 21 Oct 2005 03:20:53 -0400 Subject: [PATCH] gfp_t: block layer core Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- drivers/block/elevator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/block/elevator.c') diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 98f0126a2deb..c744d2a13062 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -487,7 +487,7 @@ struct request *elv_former_request(request_queue_t *q, struct request *rq) } int elv_set_request(request_queue_t *q, struct request *rq, struct bio *bio, - int gfp_mask) + gfp_t gfp_mask) { elevator_t *e = q->elevator; -- cgit v1.2.2