Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] optimise loop driver a bit

Looks like locking can be optimised quite a lot. Increase lock widths
slightly so lo_lock is taken fewer times per request. Also it was quite
trivial to cover lo_pending with that lock, and remove the atomic
requirement. This also makes memory ordering explicitly correct, which is
nice (not that I particularly saw any mem ordering bugs).

Test was reading 4 250MB files in parallel on ext2-on-tmpfs filesystem (1K
block size, 4K page size). System is 2 socket Xeon with HT (4 thread).

intel:/home/npiggin# umount /dev/loop0 ; mount /dev/loop0 /mnt/loop ; /usr/bin/time ./mtloop.sh

Before:
0.24user 5.51system 0:02.84elapsed 202%CPU (0avgtext+0avgdata 0maxresident)k
0.19user 5.52system 0:02.88elapsed 198%CPU (0avgtext+0avgdata 0maxresident)k
0.19user 5.57system 0:02.89elapsed 198%CPU (0avgtext+0avgdata 0maxresident)k
0.22user 5.51system 0:02.90elapsed 197%CPU (0avgtext+0avgdata 0maxresident)k
0.19user 5.44system 0:02.91elapsed 193%CPU (0avgtext+0avgdata 0maxresident)k

After:
0.07user 2.34system 0:01.68elapsed 143%CPU (0avgtext+0avgdata 0maxresident)k
0.06user 2.37system 0:01.68elapsed 144%CPU (0avgtext+0avgdata 0maxresident)k
0.06user 2.39system 0:01.68elapsed 145%CPU (0avgtext+0avgdata 0maxresident)k
0.06user 2.36system 0:01.68elapsed 144%CPU (0avgtext+0avgdata 0maxresident)k
0.06user 2.42system 0:01.68elapsed 147%CPU (0avgtext+0avgdata 0maxresident)k

Signed-off-by: Nick Piggin <nickpiggin@yahoo.com.au>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Nick Piggin and committed by
Linus Torvalds
35a82d1a ab4af03a

+39 -44
+38 -43
drivers/block/loop.c
··· 472 472 */ 473 473 static void loop_add_bio(struct loop_device *lo, struct bio *bio) 474 474 { 475 - unsigned long flags; 476 - 477 - spin_lock_irqsave(&lo->lo_lock, flags); 478 475 if (lo->lo_biotail) { 479 476 lo->lo_biotail->bi_next = bio; 480 477 lo->lo_biotail = bio; 481 478 } else 482 479 lo->lo_bio = lo->lo_biotail = bio; 483 - spin_unlock_irqrestore(&lo->lo_lock, flags); 484 - 485 - up(&lo->lo_bh_mutex); 486 480 } 487 481 488 482 /* ··· 486 492 { 487 493 struct bio *bio; 488 494 489 - spin_lock_irq(&lo->lo_lock); 490 495 if ((bio = lo->lo_bio)) { 491 496 if (bio == lo->lo_biotail) 492 497 lo->lo_biotail = NULL; 493 498 lo->lo_bio = bio->bi_next; 494 499 bio->bi_next = NULL; 495 500 } 496 - spin_unlock_irq(&lo->lo_lock); 497 501 498 502 return bio; 499 503 } ··· 501 509 struct loop_device *lo = q->queuedata; 502 510 int rw = bio_rw(old_bio); 503 511 504 - if (!lo) 505 - goto out; 512 + if (rw == READA) 513 + rw = READ; 514 + 515 + BUG_ON(!lo || (rw != READ && rw != WRITE)); 506 516 507 517 spin_lock_irq(&lo->lo_lock); 508 518 if (lo->lo_state != Lo_bound) 509 - goto inactive; 510 - atomic_inc(&lo->lo_pending); 511 - spin_unlock_irq(&lo->lo_lock); 512 - 513 - if (rw == WRITE) { 514 - if (lo->lo_flags & LO_FLAGS_READ_ONLY) 515 - goto err; 516 - } else if (rw == READA) { 517 - rw = READ; 518 - } else if (rw != READ) { 519 - printk(KERN_ERR "loop: unknown command (%x)\n", rw); 520 - goto err; 521 - } 519 + goto out; 520 + if (unlikely(rw == WRITE && (lo->lo_flags & LO_FLAGS_READ_ONLY))) 521 + goto out; 522 + lo->lo_pending++; 522 523 loop_add_bio(lo, old_bio); 524 + spin_unlock_irq(&lo->lo_lock); 525 + up(&lo->lo_bh_mutex); 523 526 return 0; 524 - err: 525 - if (atomic_dec_and_test(&lo->lo_pending)) 526 - up(&lo->lo_bh_mutex); 527 + 527 528 out: 529 + if (lo->lo_pending == 0) 530 + up(&lo->lo_bh_mutex); 531 + spin_unlock_irq(&lo->lo_lock); 528 532 bio_io_error(old_bio, old_bio->bi_size); 529 533 return 0; 530 - inactive: 531 - spin_unlock_irq(&lo->lo_lock); 532 - goto out; 533 534 } 534 535 535 536 /* ··· 545 560 546 561 static inline void loop_handle_bio(struct loop_device *lo, struct bio *bio) 547 562 { 548 - int ret; 549 - 550 563 if (unlikely(!bio->bi_bdev)) { 551 564 do_loop_switch(lo, bio->bi_private); 552 565 bio_put(bio); 553 566 } else { 554 - ret = do_bio_filebacked(lo, bio); 567 + int ret = do_bio_filebacked(lo, bio); 555 568 bio_endio(bio, bio->bi_size, ret); 556 569 } 557 570 } ··· 577 594 set_user_nice(current, -20); 578 595 579 596 lo->lo_state = Lo_bound; 580 - atomic_inc(&lo->lo_pending); 597 + lo->lo_pending = 1; 581 598 582 599 /* 583 600 * up sem, we are running ··· 585 602 up(&lo->lo_sem); 586 603 587 604 for (;;) { 588 - down_interruptible(&lo->lo_bh_mutex); 605 + int pending; 606 + 589 607 /* 590 - * could be upped because of tear-down, not because of 591 - * pending work 608 + * interruptible just to not contribute to load avg 592 609 */ 593 - if (!atomic_read(&lo->lo_pending)) 610 + if (down_interruptible(&lo->lo_bh_mutex)) 611 + continue; 612 + 613 + spin_lock_irq(&lo->lo_lock); 614 + 615 + /* 616 + * could be upped because of tear-down, not pending work 617 + */ 618 + if (unlikely(!lo->lo_pending)) { 619 + spin_unlock_irq(&lo->lo_lock); 594 620 break; 621 + } 595 622 596 623 bio = loop_get_bio(lo); 597 - if (!bio) { 598 - printk("loop: missing bio\n"); 599 - continue; 600 - } 624 + lo->lo_pending--; 625 + pending = lo->lo_pending; 626 + spin_unlock_irq(&lo->lo_lock); 627 + 628 + BUG_ON(!bio); 601 629 loop_handle_bio(lo, bio); 602 630 603 631 /* 604 632 * upped both for pending work and tear-down, lo_pending 605 633 * will hit zero then 606 634 */ 607 - if (atomic_dec_and_test(&lo->lo_pending)) 635 + if (unlikely(!pending)) 608 636 break; 609 637 } 610 638 ··· 894 900 895 901 spin_lock_irq(&lo->lo_lock); 896 902 lo->lo_state = Lo_rundown; 897 - if (atomic_dec_and_test(&lo->lo_pending)) 903 + lo->lo_pending--; 904 + if (!lo->lo_pending) 898 905 up(&lo->lo_bh_mutex); 899 906 spin_unlock_irq(&lo->lo_lock); 900 907
+1 -1
include/linux/loop.h
··· 61 61 struct semaphore lo_sem; 62 62 struct semaphore lo_ctl_mutex; 63 63 struct semaphore lo_bh_mutex; 64 - atomic_t lo_pending; 64 + int lo_pending; 65 65 66 66 request_queue_t *lo_queue; 67 67 };