diff options
| author | Jens Axboe <axboe@suse.de> | 2006-03-28 01:59:01 -0500 |
|---|---|---|
| committer | Jens Axboe <axboe@suse.de> | 2006-03-28 01:59:01 -0500 |
| commit | e2d74ac0664c89757bde8fb18c98cd7bf53da61c (patch) | |
| tree | 1e858044a9180766eae4ec694d4200c4ae850406 | |
| parent | 329b10bb0feacb7fb9a41389313ff0a51ae56f2a (diff) | |
[PATCH] [BLOCK] cfq-iosched: change cfq io context linking from list to tree
On setups with many disks, we spend a considerable amount of time
looking up the process-disk mapping on each queue of io. Testing with
a NULL based block driver, this costs 40-50% reduction in throughput
for 1000 disks.
Signed-off-by: Jens Axboe <axboe@suse.de>
| -rw-r--r-- | block/cfq-iosched.c | 205 | ||||
| -rw-r--r-- | block/ll_rw_blk.c | 19 | ||||
| -rw-r--r-- | include/linux/blkdev.h | 14 |
3 files changed, 114 insertions, 124 deletions
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index bde40a6ae6..bb43a16776 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c | |||
| @@ -1190,19 +1190,19 @@ cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned short prio) | |||
| 1190 | return __cfq_find_cfq_hash(cfqd, key, prio, hash_long(key, CFQ_QHASH_SHIFT)); | 1190 | return __cfq_find_cfq_hash(cfqd, key, prio, hash_long(key, CFQ_QHASH_SHIFT)); |
| 1191 | } | 1191 | } |
| 1192 | 1192 | ||
| 1193 | static void cfq_free_io_context(struct cfq_io_context *cic) | 1193 | static void cfq_free_io_context(struct io_context *ioc) |
| 1194 | { | 1194 | { |
| 1195 | struct cfq_io_context *__cic; | 1195 | struct cfq_io_context *__cic; |
| 1196 | struct list_head *entry, *next; | 1196 | struct rb_node *n; |
| 1197 | int freed = 1; | 1197 | int freed = 0; |
| 1198 | 1198 | ||
| 1199 | list_for_each_safe(entry, next, &cic->list) { | 1199 | while ((n = rb_first(&ioc->cic_root)) != NULL) { |
| 1200 | __cic = list_entry(entry, struct cfq_io_context, list); | 1200 | __cic = rb_entry(n, struct cfq_io_context, rb_node); |
| 1201 | rb_erase(&__cic->rb_node, &ioc->cic_root); | ||
| 1201 | kmem_cache_free(cfq_ioc_pool, __cic); | 1202 | kmem_cache_free(cfq_ioc_pool, __cic); |
| 1202 | freed++; | 1203 | freed++; |
| 1203 | } | 1204 | } |
| 1204 | 1205 | ||
| 1205 | kmem_cache_free(cfq_ioc_pool, cic); | ||
| 1206 | if (atomic_sub_and_test(freed, &ioc_count) && ioc_gone) | 1206 | if (atomic_sub_and_test(freed, &ioc_count) && ioc_gone) |
| 1207 | complete(ioc_gone); | 1207 | complete(ioc_gone); |
| 1208 | } | 1208 | } |
| @@ -1210,8 +1210,7 @@ static void cfq_free_io_context(struct cfq_io_context *cic) | |||
| 1210 | static void cfq_trim(struct io_context *ioc) | 1210 | static void cfq_trim(struct io_context *ioc) |
| 1211 | { | 1211 | { |
| 1212 | ioc->set_ioprio = NULL; | 1212 | ioc->set_ioprio = NULL; |
| 1213 | if (ioc->cic) | 1213 | cfq_free_io_context(ioc); |
| 1214 | cfq_free_io_context(ioc->cic); | ||
| 1215 | } | 1214 | } |
| 1216 | 1215 | ||
| 1217 | /* | 1216 | /* |
| @@ -1250,26 +1249,26 @@ static void cfq_exit_single_io_context(struct cfq_io_context *cic) | |||
| 1250 | spin_unlock(q->queue_lock); | 1249 | spin_unlock(q->queue_lock); |
| 1251 | } | 1250 | } |
| 1252 | 1251 | ||
| 1253 | static void cfq_exit_io_context(struct cfq_io_context *cic) | 1252 | static void cfq_exit_io_context(struct io_context *ioc) |
| 1254 | { | 1253 | { |
| 1255 | struct cfq_io_context *__cic; | 1254 | struct cfq_io_context *__cic; |
| 1256 | struct list_head *entry; | ||
| 1257 | unsigned long flags; | 1255 | unsigned long flags; |
| 1258 | 1256 | struct rb_node *n; | |
| 1259 | local_irq_save(flags); | ||
| 1260 | 1257 | ||
| 1261 | /* | 1258 | /* |
| 1262 | * put the reference this task is holding to the various queues | 1259 | * put the reference this task is holding to the various queues |
| 1263 | */ | 1260 | */ |
| 1264 | read_lock(&cfq_exit_lock); | 1261 | read_lock_irqsave(&cfq_exit_lock, flags); |
| 1265 | list_for_each(entry, &cic->list) { | 1262 | |
| 1266 | __cic = list_entry(entry, struct cfq_io_context, list); | 1263 | n = rb_first(&ioc->cic_root); |
| 1264 | while (n != NULL) { | ||
| 1265 | __cic = rb_entry(n, struct cfq_io_context, rb_node); | ||
| 1266 | |||
| 1267 | cfq_exit_single_io_context(__cic); | 1267 | cfq_exit_single_io_context(__cic); |
| 1268 | n = rb_next(n); | ||
| 1268 | } | 1269 | } |
| 1269 | 1270 | ||
| 1270 | cfq_exit_single_io_context(cic); | 1271 | read_unlock_irqrestore(&cfq_exit_lock, flags); |
| 1271 | read_unlock(&cfq_exit_lock); | ||
| 1272 | local_irq_restore(flags); | ||
| 1273 | } | 1272 | } |
| 1274 | 1273 | ||
| 1275 | static struct cfq_io_context * | 1274 | static struct cfq_io_context * |
| @@ -1278,10 +1277,10 @@ cfq_alloc_io_context(struct cfq_data *cfqd, gfp_t gfp_mask) | |||
| 1278 | struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_mask); | 1277 | struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_mask); |
| 1279 | 1278 | ||
| 1280 | if (cic) { | 1279 | if (cic) { |
| 1281 | INIT_LIST_HEAD(&cic->list); | 1280 | RB_CLEAR(&cic->rb_node); |
| 1281 | cic->key = NULL; | ||
| 1282 | cic->cfqq[ASYNC] = NULL; | 1282 | cic->cfqq[ASYNC] = NULL; |
| 1283 | cic->cfqq[SYNC] = NULL; | 1283 | cic->cfqq[SYNC] = NULL; |
| 1284 | cic->key = NULL; | ||
| 1285 | cic->last_end_request = jiffies; | 1284 | cic->last_end_request = jiffies; |
| 1286 | cic->ttime_total = 0; | 1285 | cic->ttime_total = 0; |
| 1287 | cic->ttime_samples = 0; | 1286 | cic->ttime_samples = 0; |
| @@ -1373,15 +1372,17 @@ static inline void changed_ioprio(struct cfq_io_context *cic) | |||
| 1373 | static int cfq_ioc_set_ioprio(struct io_context *ioc, unsigned int ioprio) | 1372 | static int cfq_ioc_set_ioprio(struct io_context *ioc, unsigned int ioprio) |
| 1374 | { | 1373 | { |
| 1375 | struct cfq_io_context *cic; | 1374 | struct cfq_io_context *cic; |
| 1375 | struct rb_node *n; | ||
| 1376 | 1376 | ||
| 1377 | write_lock(&cfq_exit_lock); | 1377 | write_lock(&cfq_exit_lock); |
| 1378 | 1378 | ||
| 1379 | cic = ioc->cic; | 1379 | n = rb_first(&ioc->cic_root); |
| 1380 | 1380 | while (n != NULL) { | |
| 1381 | changed_ioprio(cic); | 1381 | cic = rb_entry(n, struct cfq_io_context, rb_node); |
| 1382 | 1382 | ||
| 1383 | list_for_each_entry(cic, &cic->list, list) | ||
| 1384 | changed_ioprio(cic); | 1383 | changed_ioprio(cic); |
| 1384 | n = rb_next(n); | ||
| 1385 | } | ||
| 1385 | 1386 | ||
| 1386 | write_unlock(&cfq_exit_lock); | 1387 | write_unlock(&cfq_exit_lock); |
| 1387 | 1388 | ||
| @@ -1445,14 +1446,67 @@ out: | |||
| 1445 | return cfqq; | 1446 | return cfqq; |
| 1446 | } | 1447 | } |
| 1447 | 1448 | ||
| 1449 | static struct cfq_io_context * | ||
| 1450 | cfq_cic_rb_lookup(struct cfq_data *cfqd, struct io_context *ioc) | ||
| 1451 | { | ||
| 1452 | struct rb_node *n = ioc->cic_root.rb_node; | ||
| 1453 | struct cfq_io_context *cic; | ||
| 1454 | void *key = cfqd; | ||
| 1455 | |||
| 1456 | while (n) { | ||
| 1457 | cic = rb_entry(n, struct cfq_io_context, rb_node); | ||
| 1458 | |||
| 1459 | if (key < cic->key) | ||
| 1460 | n = n->rb_left; | ||
| 1461 | else if (key > cic->key) | ||
| 1462 | n = n->rb_right; | ||
| 1463 | else | ||
| 1464 | return cic; | ||
| 1465 | } | ||
| 1466 | |||
| 1467 | return NULL; | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | static inline void | ||
| 1471 | cfq_cic_link(struct cfq_data *cfqd, struct io_context *ioc, | ||
| 1472 | struct cfq_io_context *cic) | ||
| 1473 | { | ||
| 1474 | struct rb_node **p = &ioc->cic_root.rb_node; | ||
| 1475 | struct rb_node *parent = NULL; | ||
| 1476 | struct cfq_io_context *__cic; | ||
| 1477 | |||
| 1478 | read_lock(&cfq_exit_lock); | ||
| 1479 | |||
| 1480 | cic->ioc = ioc; | ||
| 1481 | cic->key = cfqd; | ||
| 1482 | |||
| 1483 | ioc->set_ioprio = cfq_ioc_set_ioprio; | ||
| 1484 | |||
| 1485 | while (*p) { | ||
| 1486 | parent = *p; | ||
| 1487 | __cic = rb_entry(parent, struct cfq_io_context, rb_node); | ||
| 1488 | |||
| 1489 | if (cic->key < __cic->key) | ||
| 1490 | p = &(*p)->rb_left; | ||
| 1491 | else if (cic->key > __cic->key) | ||
| 1492 | p = &(*p)->rb_right; | ||
| 1493 | else | ||
| 1494 | BUG(); | ||
| 1495 | } | ||
| 1496 | |||
| 1497 | rb_link_node(&cic->rb_node, parent, p); | ||
| 1498 | rb_insert_color(&cic->rb_node, &ioc->cic_root); | ||
| 1499 | list_add(&cic->queue_list, &cfqd->cic_list); | ||
| 1500 | read_unlock(&cfq_exit_lock); | ||
| 1501 | } | ||
| 1502 | |||
| 1448 | /* | 1503 | /* |
| 1449 | * Setup general io context and cfq io context. There can be several cfq | 1504 | * Setup general io context and cfq io context. There can be several cfq |
| 1450 | * io contexts per general io context, if this process is doing io to more | 1505 | * io contexts per general io context, if this process is doing io to more |
| 1451 | * than one device managed by cfq. Note that caller is holding a reference to | 1506 | * than one device managed by cfq. |
| 1452 | * cfqq, so we don't need to worry about it disappearing | ||
| 1453 | */ | 1507 | */ |
| 1454 | static struct cfq_io_context * | 1508 | static struct cfq_io_context * |
| 1455 | cfq_get_io_context(struct cfq_data *cfqd, pid_t pid, gfp_t gfp_mask) | 1509 | cfq_get_io_context(struct cfq_data *cfqd, gfp_t gfp_mask) |
| 1456 | { | 1510 | { |
| 1457 | struct io_context *ioc = NULL; | 1511 | struct io_context *ioc = NULL; |
| 1458 | struct cfq_io_context *cic; | 1512 | struct cfq_io_context *cic; |
| @@ -1463,88 +1517,15 @@ cfq_get_io_context(struct cfq_data *cfqd, pid_t pid, gfp_t gfp_mask) | |||
| 1463 | if (!ioc) | 1517 | if (!ioc) |
| 1464 | return NULL; | 1518 | return NULL; |
| 1465 | 1519 | ||
| 1466 | restart: | 1520 | cic = cfq_cic_rb_lookup(cfqd, ioc); |
| 1467 | if ((cic = ioc->cic) == NULL) { | 1521 | if (cic) |
| 1468 | cic = cfq_alloc_io_context(cfqd, gfp_mask); | 1522 | goto out; |
| 1469 | |||
| 1470 | if (cic == NULL) | ||
| 1471 | goto err; | ||
| 1472 | |||
| 1473 | /* | ||
| 1474 | * manually increment generic io_context usage count, it | ||
