diff options
Diffstat (limited to 'block/elevator.c')
-rw-r--r-- | block/elevator.c | 147 |
1 files changed, 132 insertions, 15 deletions
diff --git a/block/elevator.c b/block/elevator.c index 4ac97b642042..cff1102dac9d 100644 --- a/block/elevator.c +++ b/block/elevator.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/compiler.h> | 33 | #include <linux/compiler.h> |
34 | #include <linux/delay.h> | 34 | #include <linux/delay.h> |
35 | #include <linux/blktrace_api.h> | 35 | #include <linux/blktrace_api.h> |
36 | #include <linux/hash.h> | ||
36 | 37 | ||
37 | #include <asm/uaccess.h> | 38 | #include <asm/uaccess.h> |
38 | 39 | ||
@@ -40,6 +41,16 @@ static DEFINE_SPINLOCK(elv_list_lock); | |||
40 | static LIST_HEAD(elv_list); | 41 | static LIST_HEAD(elv_list); |
41 | 42 | ||
42 | /* | 43 | /* |
44 | * Merge hash stuff. | ||
45 | */ | ||
46 | static const int elv_hash_shift = 6; | ||
47 | #define ELV_HASH_BLOCK(sec) ((sec) >> 3) | ||
48 | #define ELV_HASH_FN(sec) (hash_long(ELV_HASH_BLOCK((sec)), elv_hash_shift)) | ||
49 | #define ELV_HASH_ENTRIES (1 << elv_hash_shift) | ||
50 | #define rq_hash_key(rq) ((rq)->sector + (rq)->nr_sectors) | ||
51 | #define ELV_ON_HASH(rq) (!hlist_unhashed(&(rq)->hash)) | ||
52 | |||
53 | /* | ||
43 | * can we safely merge with this request? | 54 | * can we safely merge with this request? |
44 | */ | 55 | */ |
45 | inline int elv_rq_merge_ok(struct request *rq, struct bio *bio) | 56 | inline int elv_rq_merge_ok(struct request *rq, struct bio *bio) |
@@ -153,25 +164,41 @@ static struct kobj_type elv_ktype; | |||
153 | 164 | ||
154 | static elevator_t *elevator_alloc(struct elevator_type *e) | 165 | static elevator_t *elevator_alloc(struct elevator_type *e) |
155 | { | 166 | { |
156 | elevator_t *eq = kmalloc(sizeof(elevator_t), GFP_KERNEL); | 167 | elevator_t *eq; |
157 | if (eq) { | 168 | int i; |
158 | memset(eq, 0, sizeof(*eq)); | 169 | |
159 | eq->ops = &e->ops; | 170 | eq = kmalloc(sizeof(elevator_t), GFP_KERNEL); |
160 | eq->elevator_type = e; | 171 | if (unlikely(!eq)) |
161 | kobject_init(&eq->kobj); | 172 | goto err; |
162 | snprintf(eq->kobj.name, KOBJ_NAME_LEN, "%s", "iosched"); | 173 | |
163 | eq->kobj.ktype = &elv_ktype; | 174 | memset(eq, 0, sizeof(*eq)); |
164 | mutex_init(&eq->sysfs_lock); | 175 | eq->ops = &e->ops; |
165 | } else { | 176 | eq->elevator_type = e; |
166 | elevator_put(e); | 177 | kobject_init(&eq->kobj); |
167 | } | 178 | snprintf(eq->kobj.name, KOBJ_NAME_LEN, "%s", "iosched"); |
179 | eq->kobj.ktype = &elv_ktype; | ||
180 | mutex_init(&eq->sysfs_lock); | ||
181 | |||
182 | eq->hash = kmalloc(sizeof(struct hlist_head) * ELV_HASH_ENTRIES, GFP_KERNEL); | ||
183 | if (!eq->hash) | ||
184 | goto err; | ||
185 | |||
186 | for (i = 0; i < ELV_HASH_ENTRIES; i++) | ||
187 | INIT_HLIST_HEAD(&eq->hash[i]); | ||
188 | |||
168 | return eq; | 189 | return eq; |
190 | err: | ||
191 | kfree(eq); | ||
192 | elevator_put(e); | ||
193 | return NULL; | ||
169 | } | 194 | } |
170 | 195 | ||
171 | static void elevator_release(struct kobject *kobj) | 196 | static void elevator_release(struct kobject *kobj) |
172 | { | 197 | { |
173 | elevator_t *e = container_of(kobj, elevator_t, kobj); | 198 | elevator_t *e = container_of(kobj, elevator_t, kobj); |
199 | |||
174 | elevator_put(e->elevator_type); | 200 | elevator_put(e->elevator_type); |
201 | kfree(e->hash); | ||
175 | kfree(e); | 202 | kfree(e); |
176 | } | 203 | } |
177 | 204 | ||
@@ -223,6 +250,53 @@ void elevator_exit(elevator_t *e) | |||
223 | kobject_put(&e->kobj); | 250 | kobject_put(&e->kobj); |
224 | } | 251 | } |
225 | 252 | ||
253 | static inline void __elv_rqhash_del(struct request *rq) | ||
254 | { | ||
255 | hlist_del_init(&rq->hash); | ||
256 | } | ||
257 | |||
258 | static void elv_rqhash_del(request_queue_t *q, struct request *rq) | ||
259 | { | ||
260 | if (ELV_ON_HASH(rq)) | ||
261 | __elv_rqhash_del(rq); | ||
262 | } | ||
263 | |||
264 | static void elv_rqhash_add(request_queue_t *q, struct request *rq) | ||
265 | { | ||
266 | elevator_t *e = q->elevator; | ||
267 | |||
268 | BUG_ON(ELV_ON_HASH(rq)); | ||
269 | hlist_add_head(&rq->hash, &e->hash[ELV_HASH_FN(rq_hash_key(rq))]); | ||
270 | } | ||
271 | |||
272 | static void elv_rqhash_reposition(request_queue_t *q, struct request *rq) | ||
273 | { | ||
274 | __elv_rqhash_del(rq); | ||
275 | elv_rqhash_add(q, rq); | ||
276 | } | ||
277 | |||
278 | static struct request *elv_rqhash_find(request_queue_t *q, sector_t offset) | ||
279 | { | ||
280 | elevator_t *e = q->elevator; | ||
281 | struct hlist_head *hash_list = &e->hash[ELV_HASH_FN(offset)]; | ||
282 | struct hlist_node *entry, *next; | ||
283 | struct request *rq; | ||
284 | |||
285 | hlist_for_each_entry_safe(rq, entry, next, hash_list, hash) { | ||
286 | BUG_ON(!ELV_ON_HASH(rq)); | ||
287 | |||
288 | if (unlikely(!rq_mergeable(rq))) { | ||
289 | __elv_rqhash_del(rq); | ||
290 | continue; | ||
291 | } | ||
292 | |||
293 | if (rq_hash_key(rq) == offset) | ||
294 | return rq; | ||
295 | } | ||
296 | |||
297 | return NULL; | ||
298 | } | ||
299 | |||
226 | /* | 300 | /* |
227 | * Insert rq into dispatch queue of q. Queue lock must be held on | 301 | * Insert rq into dispatch queue of q. Queue lock must be held on |
228 | * entry. If sort != 0, rq is sort-inserted; otherwise, rq will be | 302 | * entry. If sort != 0, rq is sort-inserted; otherwise, rq will be |
@@ -235,6 +309,9 @@ void elv_dispatch_sort(request_queue_t *q, struct request *rq) | |||
235 | 309 | ||
236 | if (q->last_merge == rq) | 310 | if (q->last_merge == rq) |
237 | q->last_merge = NULL; | 311 | q->last_merge = NULL; |
312 | |||
313 | elv_rqhash_del(q, rq); | ||
314 | |||
238 | q->nr_sorted--; | 315 | q->nr_sorted--; |
239 | 316 | ||
240 | boundary = q->end_sector; | 317 | boundary = q->end_sector; |
@@ -258,11 +335,32 @@ void elv_dispatch_sort(request_queue_t *q, struct request *rq) | |||
258 | list_add(&rq->queuelist, entry); | 335 | list_add(&rq->queuelist, entry); |
259 | } | 336 | } |
260 | 337 | ||
338 | /* | ||
339 | * This should be in elevator.h, but that requires pulling in rq and q | ||
340 | */ | ||
341 | void elv_dispatch_add_tail(struct request_queue *q, struct request *rq) | ||
342 | { | ||
343 | if (q->last_merge == rq) | ||
344 | q->last_merge = NULL; | ||
345 | |||
346 | elv_rqhash_del(q, rq); | ||
347 | |||
348 | q->nr_sorted--; | ||
349 | |||
350 | q->end_sector = rq_end_sector(rq); | ||
351 | q->boundary_rq = rq; | ||
352 | list_add_tail(&rq->queuelist, &q->queue_head); | ||
353 | } | ||
354 | |||
261 | int elv_merge(request_queue_t *q, struct request **req, struct bio *bio) | 355 | int elv_merge(request_queue_t *q, struct request **req, struct bio *bio) |
262 | { | 356 | { |
263 | elevator_t *e = q->elevator; | 357 | elevator_t *e = q->elevator; |
358 | struct request *__rq; | ||
264 | int ret; | 359 | int ret; |
265 | 360 | ||
361 | /* | ||
362 | * First try one-hit cache. | ||
363 | */ | ||
266 | if (q->last_merge) { | 364 | if (q->last_merge) { |
267 | ret = elv_try_merge(q->last_merge, bio); | 365 | ret = elv_try_merge(q->last_merge, bio); |
268 | if (ret != ELEVATOR_NO_MERGE) { | 366 | if (ret != ELEVATOR_NO_MERGE) { |
@@ -271,6 +369,15 @@ int elv_merge(request_queue_t *q, struct request **req, struct bio *bio) | |||
271 | } | 369 | } |
272 | } | 370 | } |
273 | 371 | ||
372 | /* | ||
373 | * See if our hash lookup can find a potential backmerge. | ||
374 | */ | ||
375 | __rq = elv_rqhash_find(q, bio->bi_sector); | ||
376 | if (__rq && elv_rq_merge_ok(__rq, bio)) { | ||
377 | *req = __rq; | ||
378 | return ELEVATOR_BACK_MERGE; | ||
379 | } | ||
380 | |||
274 | if (e->ops->elevator_merge_fn) | 381 | if (e->ops->elevator_merge_fn) |
275 | return e->ops->elevator_merge_fn(q, req, bio); | 382 | return e->ops->elevator_merge_fn(q, req, bio); |
276 | 383 | ||
@@ -284,6 +391,8 @@ void elv_merged_request(request_queue_t *q, struct request *rq) | |||
284 | if (e->ops->elevator_merged_fn) | 391 | if (e->ops->elevator_merged_fn) |
285 | e->ops->elevator_merged_fn(q, rq); | 392 | e->ops->elevator_merged_fn(q, rq); |
286 | 393 | ||
394 | elv_rqhash_reposition(q, rq); | ||
395 | |||
287 | q->last_merge = rq; | 396 | q->last_merge = rq; |
288 | } | 397 | } |
289 | 398 | ||
@@ -294,8 +403,11 @@ void elv_merge_requests(request_queue_t *q, struct request *rq, | |||
294 | 403 | ||
295 | if (e->ops->elevator_merge_req_fn) | 404 | if (e->ops->elevator_merge_req_fn) |
296 | e->ops->elevator_merge_req_fn(q, rq, next); | 405 | e->ops->elevator_merge_req_fn(q, rq, next); |
297 | q->nr_sorted--; | ||
298 | 406 | ||
407 | elv_rqhash_reposition(q, rq); | ||
408 | elv_rqhash_del(q, next); | ||
409 | |||
410 | q->nr_sorted--; | ||
299 | q->last_merge = rq; | 411 | q->last_merge = rq; |
300 | } | 412 | } |
301 | 413 | ||
@@ -371,8 +483,12 @@ void elv_insert(request_queue_t *q, struct request *rq, int where) | |||
371 | BUG_ON(!blk_fs_request(rq)); | 483 | BUG_ON(!blk_fs_request(rq)); |
372 | rq->cmd_flags |= REQ_SORTED; | 484 | rq->cmd_flags |= REQ_SORTED; |
373 | q->nr_sorted++; | 485 | q->nr_sorted++; |
374 | if (q->last_merge == NULL && rq_mergeable(rq)) | 486 | if (rq_mergeable(rq)) { |
375 | q->last_merge = rq; | 487 | elv_rqhash_add(q, rq); |
488 | if (!q->last_merge) | ||
489 | q->last_merge = rq; | ||
490 | } | ||
491 | |||
376 | /* | 492 | /* |
377 | * Some ioscheds (cfq) run q->request_fn directly, so | 493 | * Some ioscheds (cfq) run q->request_fn directly, so |
378 | * rq cannot be accessed after calling | 494 | * rq cannot be accessed after calling |
@@ -557,6 +673,7 @@ struct request *elv_next_request(request_queue_t *q) | |||
557 | void elv_dequeue_request(request_queue_t *q, struct request *rq) | 673 | void elv_dequeue_request(request_queue_t *q, struct request *rq) |
558 | { | 674 | { |
559 | BUG_ON(list_empty(&rq->queuelist)); | 675 | BUG_ON(list_empty(&rq->queuelist)); |
676 | BUG_ON(ELV_ON_HASH(rq)); | ||
560 | 677 | ||
561 | list_del_init(&rq->queuelist); | 678 | list_del_init(&rq->queuelist); |
562 | 679 | ||