diff options
author | Yan Zheng <zheng.yan@oracle.com> | 2008-10-30 14:25:28 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-10-30 14:25:28 -0400 |
commit | d899e05215178fed903ad0e7fc1cb4d8e0cc0a88 (patch) | |
tree | 2969e3558f5c50ec0f9ac4201099c0d5d1d6e2c2 /fs/btrfs/file.c | |
parent | 80ff385665b7fca29fefe358a60ab0d09f9b8e87 (diff) |
Btrfs: Add fallocate support v2
This patch updates btrfs-progs for fallocate support.
fallocate is a little different in Btrfs because we need to tell the
COW system that a given preallocated extent doesn't need to be
cow'd as long as there are no snapshots of it. This leverages the
-o nodatacow checks.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Diffstat (limited to 'fs/btrfs/file.c')
-rw-r--r-- | fs/btrfs/file.c | 245 |
1 files changed, 241 insertions, 4 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 1a0510ad030c..238a8e215eb9 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c | |||
@@ -381,7 +381,7 @@ int noinline btrfs_drop_extents(struct btrfs_trans_handle *trans, | |||
381 | int keep; | 381 | int keep; |
382 | int slot; | 382 | int slot; |
383 | int bookend; | 383 | int bookend; |
384 | int found_type; | 384 | int found_type = 0; |
385 | int found_extent; | 385 | int found_extent; |
386 | int found_inline; | 386 | int found_inline; |
387 | int recow; | 387 | int recow; |
@@ -442,7 +442,8 @@ next_slot: | |||
442 | extent); | 442 | extent); |
443 | other_encoding = btrfs_file_extent_other_encoding(leaf, | 443 | other_encoding = btrfs_file_extent_other_encoding(leaf, |
444 | extent); | 444 | extent); |
445 | if (found_type == BTRFS_FILE_EXTENT_REG) { | 445 | if (found_type == BTRFS_FILE_EXTENT_REG || |
446 | found_type == BTRFS_FILE_EXTENT_PREALLOC) { | ||
446 | extent_end = | 447 | extent_end = |
447 | btrfs_file_extent_disk_bytenr(leaf, | 448 | btrfs_file_extent_disk_bytenr(leaf, |
448 | extent); | 449 | extent); |
@@ -609,8 +610,7 @@ next_slot: | |||
609 | */ | 610 | */ |
610 | btrfs_set_file_extent_ram_bytes(leaf, extent, | 611 | btrfs_set_file_extent_ram_bytes(leaf, extent, |
611 | ram_bytes); | 612 | ram_bytes); |
612 | btrfs_set_file_extent_type(leaf, extent, | 613 | btrfs_set_file_extent_type(leaf, extent, found_type); |
613 | BTRFS_FILE_EXTENT_REG); | ||
614 | 614 | ||
615 | btrfs_mark_buffer_dirty(path->nodes[0]); | 615 | btrfs_mark_buffer_dirty(path->nodes[0]); |
616 | 616 | ||
@@ -661,6 +661,243 @@ out: | |||
661 | return ret; | 661 | return ret; |
662 | } | 662 | } |
663 | 663 | ||
664 | static int extent_mergeable(struct extent_buffer *leaf, int slot, | ||
665 | u64 objectid, u64 bytenr, u64 *start, u64 *end) | ||
666 | { | ||
667 | struct btrfs_file_extent_item *fi; | ||
668 | struct btrfs_key key; | ||
669 | u64 extent_end; | ||
670 | |||
671 | if (slot < 0 || slot >= btrfs_header_nritems(leaf)) | ||
672 | return 0; | ||
673 | |||
674 | btrfs_item_key_to_cpu(leaf, &key, slot); | ||
675 | if (key.objectid != objectid || key.type != BTRFS_EXTENT_DATA_KEY) | ||
676 | return 0; | ||
677 | |||
678 | fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); | ||
679 | if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG || | ||
680 | btrfs_file_extent_disk_bytenr(leaf, fi) != bytenr || | ||
681 | btrfs_file_extent_compression(leaf, fi) || | ||
682 | btrfs_file_extent_encryption(leaf, fi) || | ||
683 | btrfs_file_extent_other_encoding(leaf, fi)) | ||
684 | return 0; | ||
685 | |||
686 | extent_end = key.offset + btrfs_file_extent_num_bytes(leaf, fi); | ||
687 | if ((*start && *start != key.offset) || (*end && *end != extent_end)) | ||
688 | return 0; | ||
689 | |||
690 | *start = key.offset; | ||
691 | *end = extent_end; | ||
692 | return 1; | ||
693 | } | ||
694 | |||
695 | /* | ||
696 | * Mark extent in the range start - end as written. | ||
697 | * | ||
698 | * This changes extent type from 'pre-allocated' to 'regular'. If only | ||
699 | * part of extent is marked as written, the extent will be split into | ||
700 | * two or three. | ||
701 | */ | ||
702 | int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, | ||
703 | struct btrfs_root *root, | ||
704 | struct inode *inode, u64 start, u64 end) | ||
705 | { | ||
706 | struct extent_buffer *leaf; | ||
707 | struct btrfs_path *path; | ||
708 | struct btrfs_file_extent_item *fi; | ||
709 | struct btrfs_key key; | ||
710 | u64 bytenr; | ||
711 | u64 num_bytes; | ||
712 | u64 extent_end; | ||
713 | u64 extent_offset; | ||
714 | u64 other_start; | ||
715 | u64 other_end; | ||
716 | u64 split = start; | ||
717 | u64 locked_end = end; | ||
718 | int extent_type; | ||
719 | int split_end = 1; | ||
720 | int ret; | ||
721 | |||
722 | btrfs_drop_extent_cache(inode, start, end - 1, 0); | ||
723 | |||
724 | path = btrfs_alloc_path(); | ||
725 | BUG_ON(!path); | ||
726 | again: | ||
727 | key.objectid = inode->i_ino; | ||
728 | key.type = BTRFS_EXTENT_DATA_KEY; | ||
729 | if (split == start) | ||
730 | key.offset = split; | ||
731 | else | ||
732 | key.offset = split - 1; | ||
733 | |||
734 | ret = btrfs_search_slot(trans, root, &key, path, -1, 1); | ||
735 | if (ret > 0 && path->slots[0] > 0) | ||
736 | path->slots[0]--; | ||
737 | |||
738 | leaf = path->nodes[0]; | ||
739 | btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); | ||
740 | BUG_ON(key.objectid != inode->i_ino || | ||
741 | key.type != BTRFS_EXTENT_DATA_KEY); | ||
742 | fi = btrfs_item_ptr(leaf, path->slots[0], | ||
743 | struct btrfs_file_extent_item); | ||
744 | extent_type = btrfs_file_extent_type(leaf, fi); | ||
745 | BUG_ON(extent_type != BTRFS_FILE_EXTENT_PREALLOC); | ||
746 | extent_end = key.offset + btrfs_file_extent_num_bytes(leaf, fi); | ||
747 | BUG_ON(key.offset > start || extent_end < end); | ||
748 | |||
749 | bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); | ||
750 | num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi); | ||
751 | extent_offset = btrfs_file_extent_offset(leaf, fi); | ||
752 | |||
753 | if (key.offset == start) | ||
754 | split = end; | ||
755 | |||
756 | if (key.offset == start && extent_end == end) { | ||
757 | int del_nr = 0; | ||
758 | int del_slot = 0; | ||
759 | u64 leaf_owner = btrfs_header_owner(leaf); | ||
760 | u64 leaf_gen = btrfs_header_generation(leaf); | ||
761 | other_start = end; | ||
762 | other_end = 0; | ||
763 | if (extent_mergeable(leaf, path->slots[0] + 1, inode->i_ino, | ||
764 | bytenr, &other_start, &other_end)) { | ||
765 | extent_end = other_end; | ||
766 | del_slot = path->slots[0] + 1; | ||
767 | del_nr++; | ||
768 | ret = btrfs_free_extent(trans, root, bytenr, num_bytes, | ||
769 | leaf->start, leaf_owner, | ||
770 | leaf_gen, inode->i_ino, 0); | ||
771 | BUG_ON(ret); | ||
772 | } | ||
773 | other_start = 0; | ||
774 | other_end = start; | ||
775 | if (extent_mergeable(leaf, path->slots[0] - 1, inode->i_ino, | ||
776 | bytenr, &other_start, &other_end)) { | ||
777 | key.offset = other_start; | ||
778 | del_slot = path->slots[0]; | ||
779 | del_nr++; | ||
780 | ret = btrfs_free_extent(trans, root, bytenr, num_bytes, | ||
781 | leaf->start, leaf_owner, | ||
782 | leaf_gen, inode->i_ino, 0); | ||
783 | BUG_ON(ret); | ||
784 | } | ||
785 | split_end = 0; | ||
786 | if (del_nr == 0) { | ||
787 | btrfs_set_file_extent_type(leaf, fi, | ||
788 | BTRFS_FILE_EXTENT_REG); | ||
789 | goto done; | ||
790 | } | ||
791 | |||
792 | fi = btrfs_item_ptr(leaf, del_slot - 1, | ||
793 | struct btrfs_file_extent_item); | ||
794 | btrfs_set_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG); | ||
795 | btrfs_set_file_extent_num_bytes(leaf, fi, | ||
796 | extent_end - key.offset); | ||
797 | btrfs_mark_buffer_dirty(leaf); | ||
798 | |||
799 | ret = btrfs_del_items(trans, root, path, del_slot, del_nr); | ||
800 | BUG_ON(ret); | ||
801 | goto done; | ||
802 | } else if (split == start) { | ||
803 | if (locked_end < extent_end) { | ||
804 | ret = try_lock_extent(&BTRFS_I(inode)->io_tree, | ||
805 | locked_end, extent_end - 1, GFP_NOFS); | ||
806 | if (!ret) { | ||
807 | btrfs_release_path(root, path); | ||
808 | lock_extent(&BTRFS_I(inode)->io_tree, | ||
809 | locked_end, extent_end - 1, GFP_NOFS); | ||
810 | locked_end = extent_end; | ||
811 | goto again; | ||
812 | } | ||
813 | locked_end = extent_end; | ||
814 | } | ||
815 | btrfs_set_file_extent_num_bytes(leaf, fi, split - key.offset); | ||
816 | extent_offset += split - key.offset; | ||
817 | } else { | ||
818 | BUG_ON(key.offset != start); | ||
819 | btrfs_set_file_extent_offset(leaf, fi, extent_offset + | ||
820 | split - key.offset); | ||
821 | btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - split); | ||
822 | key.offset = split; | ||
823 | btrfs_set_item_key_safe(trans, root, path, &key); | ||
824 | extent_end = split; | ||
825 | } | ||
826 | |||
827 | if (extent_end == end) { | ||
828 | split_end = 0; | ||
829 | extent_type = BTRFS_FILE_EXTENT_REG; | ||
830 | } | ||
831 | if (extent_end == end && split == start) { | ||
832 | other_start = end; | ||
833 | other_end = 0; | ||
834 | if (extent_mergeable(leaf, path->slots[0] + 1, inode->i_ino, | ||
835 | bytenr, &other_start, &other_end)) { | ||
836 | path->slots[0]++; | ||
837 | fi = btrfs_item_ptr(leaf, path->slots[0], | ||
838 | struct btrfs_file_extent_item); | ||
839 | key.offset = split; | ||
840 | btrfs_set_item_key_safe(trans, root, path, &key); | ||
841 | btrfs_set_file_extent_offset(leaf, fi, extent_offset); | ||
842 | btrfs_set_file_extent_num_bytes(leaf, fi, | ||
843 | other_end - split); | ||
844 | goto done; | ||
845 | } | ||
846 | } | ||
847 | if (extent_end == end && split == end) { | ||
848 | other_start = 0; | ||
849 | other_end = start; | ||
850 | if (extent_mergeable(leaf, path->slots[0] - 1 , inode->i_ino, | ||
851 | bytenr, &other_start, &other_end)) { | ||
852 | path->slots[0]--; | ||
853 | fi = btrfs_item_ptr(leaf, path->slots[0], | ||
854 | struct btrfs_file_extent_item); | ||
855 | btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - | ||
856 | other_start); | ||
857 | goto done; | ||
858 | } | ||
859 | } | ||
860 | |||
861 | btrfs_mark_buffer_dirty(leaf); | ||
862 | btrfs_release_path(root, path); | ||
863 | |||
864 | key.offset = start; | ||
865 | ret = btrfs_insert_empty_item(trans, root, path, &key, sizeof(*fi)); | ||
866 | BUG_ON(ret); | ||
867 | |||
868 | leaf = path->nodes[0]; | ||
869 | fi = btrfs_item_ptr(leaf, path->slots[0], | ||
870 | struct btrfs_file_extent_item); | ||
871 | btrfs_set_file_extent_generation(leaf, fi, trans->transid); | ||
872 | btrfs_set_file_extent_type(leaf, fi, extent_type); | ||
873 | btrfs_set_file_extent_disk_bytenr(leaf, fi, bytenr); | ||
874 | btrfs_set_file_extent_disk_num_bytes(leaf, fi, num_bytes); | ||
875 | btrfs_set_file_extent_offset(leaf, fi, extent_offset); | ||
876 | btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - key.offset); | ||
877 | btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes); | ||
878 | btrfs_set_file_extent_compression(leaf, fi, 0); | ||
879 | btrfs_set_file_extent_encryption(leaf, fi, 0); | ||
880 | btrfs_set_file_extent_other_encoding(leaf, fi, 0); | ||
881 | |||
882 | ret = btrfs_inc_extent_ref(trans, root, bytenr, num_bytes, | ||
883 | leaf->start, root->root_key.objectid, | ||
884 | trans->transid, inode->i_ino); | ||
885 | BUG_ON(ret); | ||
886 | done: | ||
887 | btrfs_mark_buffer_dirty(leaf); | ||
888 | btrfs_release_path(root, path); | ||
889 | if (split_end && split == start) { | ||
890 | split = end; | ||
891 | goto again; | ||
892 | } | ||
893 | if (locked_end > end) { | ||
894 | unlock_extent(&BTRFS_I(inode)->io_tree, end, locked_end - 1, | ||
895 | GFP_NOFS); | ||
896 | } | ||
897 | btrfs_free_path(path); | ||
898 | return 0; | ||
899 | } | ||
900 | |||
664 | /* | 901 | /* |
665 | * this gets pages into the page cache and locks them down, it also properly | 902 | * this gets pages into the page cache and locks them down, it also properly |
666 | * waits for data=ordered extents to finish before allowing the pages to be | 903 | * waits for data=ordered extents to finish before allowing the pages to be |