diff options
| -rw-r--r-- | fs/btrfs/disk-io.c | 145 | ||||
| -rw-r--r-- | fs/btrfs/volumes.h | 6 |
2 files changed, 134 insertions, 17 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index b6a5c0dd0dd8..48d30138237f 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
| @@ -2573,22 +2573,10 @@ static int write_dev_supers(struct btrfs_device *device, | |||
| 2573 | int errors = 0; | 2573 | int errors = 0; |
| 2574 | u32 crc; | 2574 | u32 crc; |
| 2575 | u64 bytenr; | 2575 | u64 bytenr; |
| 2576 | int last_barrier = 0; | ||
| 2577 | 2576 | ||
| 2578 | if (max_mirrors == 0) | 2577 | if (max_mirrors == 0) |
| 2579 | max_mirrors = BTRFS_SUPER_MIRROR_MAX; | 2578 | max_mirrors = BTRFS_SUPER_MIRROR_MAX; |
| 2580 | 2579 | ||
| 2581 | /* make sure only the last submit_bh does a barrier */ | ||
| 2582 | if (do_barriers) { | ||
| 2583 | for (i = 0; i < max_mirrors; i++) { | ||
| 2584 | bytenr = btrfs_sb_offset(i); | ||
| 2585 | if (bytenr + BTRFS_SUPER_INFO_SIZE >= | ||
| 2586 | device->total_bytes) | ||
| 2587 | break; | ||
| 2588 | last_barrier = i; | ||
| 2589 | } | ||
| 2590 | } | ||
| 2591 | |||
| 2592 | for (i = 0; i < max_mirrors; i++) { | 2580 | for (i = 0; i < max_mirrors; i++) { |
| 2593 | bytenr = btrfs_sb_offset(i); | 2581 | bytenr = btrfs_sb_offset(i); |
| 2594 | if (bytenr + BTRFS_SUPER_INFO_SIZE >= device->total_bytes) | 2582 | if (bytenr + BTRFS_SUPER_INFO_SIZE >= device->total_bytes) |
| @@ -2634,17 +2622,136 @@ static int write_dev_supers(struct btrfs_device *device, | |||
| 2634 | bh->b_end_io = btrfs_end_buffer_write_sync; | 2622 | bh->b_end_io = btrfs_end_buffer_write_sync; |
| 2635 | } | 2623 | } |
| 2636 | 2624 | ||
| 2637 | if (i == last_barrier && do_barriers) | 2625 | /* |
| 2638 | ret = submit_bh(WRITE_FLUSH_FUA, bh); | 2626 | * we fua the first super. The others we allow |
| 2639 | else | 2627 | * to go down lazy. |
| 2640 | ret = submit_bh(WRITE_SYNC, bh); | 2628 | */ |
| 2641 | 2629 | ret = submit_bh(WRITE_FUA, bh); | |
| 2642 | if (ret) | 2630 | if (ret) |
| 2643 | errors++; | 2631 | errors++; |
| 2644 | } | 2632 | } |
| 2645 | return errors < i ? 0 : -1; | 2633 | return errors < i ? 0 : -1; |
| 2646 | } | 2634 | } |
| 2647 | 2635 | ||
| 2636 | /* | ||
| 2637 | * endio for the write_dev_flush, this will wake anyone waiting | ||
| 2638 | * for the barrier when it is done | ||
| 2639 | */ | ||
| 2640 | static void btrfs_end_empty_barrier(struct bio *bio, int err) | ||
| 2641 | { | ||
| 2642 | if (err) { | ||
| 2643 | if (err == -EOPNOTSUPP) | ||
| 2644 | set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); | ||
| 2645 | clear_bit(BIO_UPTODATE, &bio->bi_flags); | ||
| 2646 | } | ||
| 2647 | if (bio->bi_private) | ||
| 2648 | complete(bio->bi_private); | ||
| 2649 | bio_put(bio); | ||
| 2650 | } | ||
| 2651 | |||
| 2652 | /* | ||
| 2653 | * trigger flushes for one the devices. If you pass wait == 0, the flushes are | ||
| 2654 | * sent down. With wait == 1, it waits for the previous flush. | ||
| 2655 | * | ||
| 2656 | * any device where the flush fails with eopnotsupp are flagged as not-barrier | ||
| 2657 | * capable | ||
| 2658 | */ | ||
| 2659 | static int write_dev_flush(struct btrfs_device *device, int wait) | ||
| 2660 | { | ||
| 2661 | struct bio *bio; | ||
| 2662 | int ret = 0; | ||
| 2663 | |||
| 2664 | if (device->nobarriers) | ||
| 2665 | return 0; | ||
| 2666 | |||
| 2667 | if (wait) { | ||
| 2668 | bio = device->flush_bio; | ||
| 2669 | if (!bio) | ||
| 2670 | return 0; | ||
| 2671 | |||
| 2672 | wait_for_completion(&device->flush_wait); | ||
| 2673 | |||
| 2674 | if (bio_flagged(bio, BIO_EOPNOTSUPP)) { | ||
| 2675 | printk("btrfs: disabling barriers on dev %s\n", | ||
| 2676 | device->name); | ||
| 2677 | device->nobarriers = 1; | ||
| 2678 | } | ||
| 2679 | if (!bio_flagged(bio, BIO_UPTODATE)) { | ||
| 2680 | ret = -EIO; | ||
| 2681 | } | ||
| 2682 | |||
| 2683 | /* drop the reference from the wait == 0 run */ | ||
| 2684 | bio_put(bio); | ||
| 2685 | device->flush_bio = NULL; | ||
| 2686 | |||
| 2687 | return ret; | ||
| 2688 | } | ||
| 2689 | |||
| 2690 | /* | ||
| 2691 | * one reference for us, and we leave it for the | ||
| 2692 | * caller | ||
| 2693 | */ | ||
| 2694 | device->flush_bio = NULL;; | ||
| 2695 | bio = bio_alloc(GFP_NOFS, 0); | ||
| 2696 | if (!bio) | ||
| 2697 | return -ENOMEM; | ||
| 2698 | |||
| 2699 | bio->bi_end_io = btrfs_end_empty_barrier; | ||
| 2700 | bio->bi_bdev = device->bdev; | ||
| 2701 | init_completion(&device->flush_wait); | ||
| 2702 | bio->bi_private = &device->flush_wait; | ||
| 2703 | device->flush_bio = bio; | ||
| 2704 | |||
| 2705 | bio_get(bio); | ||
| 2706 | submit_bio(WRITE_FLUSH, bio); | ||
| 2707 | |||
| 2708 | return 0; | ||
| 2709 | } | ||
| 2710 | |||
| 2711 | /* | ||
| 2712 | * send an empty flush down to each device in parallel, | ||
| 2713 | * then wait for them | ||
| 2714 | */ | ||
| 2715 | static int barrier_all_devices(struct btrfs_fs_info *info) | ||
| 2716 | { | ||
| 2717 | struct list_head *head; | ||
| 2718 | struct btrfs_device *dev; | ||
| 2719 | int errors = 0; | ||
| 2720 | int ret; | ||
| 2721 | |||
| 2722 | /* send down all the barriers */ | ||
| 2723 | head = &info->fs_devices->devices; | ||
| 2724 | list_for_each_entry_rcu(dev, head, dev_list) { | ||
| 2725 | if (!dev->bdev) { | ||
| 2726 | errors++; | ||
| 2727 | continue; | ||
| 2728 | } | ||
| 2729 | if (!dev->in_fs_metadata || !dev->writeable) | ||
| 2730 | continue; | ||
| 2731 | |||
| 2732 | ret = write_dev_flush(dev, 0); | ||
| 2733 | if (ret) | ||
| 2734 | errors++; | ||
| 2735 | } | ||
| 2736 | |||
| 2737 | /* wait for all the barriers */ | ||
| 2738 | list_for_each_entry_rcu(dev, head, dev_list) { | ||
| 2739 | if (!dev->bdev) { | ||
| 2740 | errors++; | ||
| 2741 | continue; | ||
| 2742 | } | ||
| 2743 | if (!dev->in_fs_metadata || !dev->writeable) | ||
| 2744 | continue; | ||
| 2745 | |||
| 2746 | ret = write_dev_flush(dev, 1); | ||
| 2747 | if (ret) | ||
| 2748 | errors++; | ||
| 2749 | } | ||
| 2750 | if (errors) | ||
| 2751 | return -EIO; | ||
| 2752 | return 0; | ||
| 2753 | } | ||
| 2754 | |||
| 2648 | int write_all_supers(struct btrfs_root *root, int max_mirrors) | 2755 | int write_all_supers(struct btrfs_root *root, int max_mirrors) |
| 2649 | { | 2756 | { |
| 2650 | struct list_head *head; | 2757 | struct list_head *head; |
| @@ -2666,6 +2773,10 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors) | |||
| 2666 | 2773 | ||
| 2667 | mutex_lock(&root->fs_info->fs_devices->device_list_mutex); | 2774 | mutex_lock(&root->fs_info->fs_devices->device_list_mutex); |
| 2668 | head = &root->fs_info->fs_devices->devices; | 2775 | head = &root->fs_info->fs_devices->devices; |
| 2776 | |||
| 2777 | if (do_barriers) | ||
| 2778 | barrier_all_devices(root->fs_info); | ||
| 2779 | |||
| 2669 | list_for_each_entry_rcu(dev, head, dev_list) { | 2780 | list_for_each_entry_rcu(dev, head, dev_list) { |
| 2670 | if (!dev->bdev) { | 2781 | if (!dev->bdev) { |
| 2671 | total_errors++; | 2782 | total_errors++; |
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index ab5b1c49f352..78f2d4d4f37f 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h | |||
| @@ -100,6 +100,12 @@ struct btrfs_device { | |||
| 100 | struct reada_zone *reada_curr_zone; | 100 | struct reada_zone *reada_curr_zone; |
| 101 | struct radix_tree_root reada_zones; | 101 | struct radix_tree_root reada_zones; |
| 102 | struct radix_tree_root reada_extents; | 102 | struct radix_tree_root reada_extents; |
| 103 | |||
| 104 | /* for sending down flush barriers */ | ||
| 105 | struct bio *flush_bio; | ||
| 106 | struct completion flush_wait; | ||
| 107 | int nobarriers; | ||
| 108 | |||
| 103 | }; | 109 | }; |
| 104 | 110 | ||
| 105 | struct btrfs_fs_devices { | 111 | struct btrfs_fs_devices { |
