diff options
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/extent_io.c | 84 | ||||
-rw-r--r-- | fs/btrfs/extent_io.h | 2 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 7 | ||||
-rw-r--r-- | fs/btrfs/ioctl.c | 2 | ||||
-rw-r--r-- | fs/btrfs/super.c | 42 |
5 files changed, 104 insertions, 33 deletions
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 77c65a0bea34..8dcfb77678de 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c | |||
@@ -690,6 +690,17 @@ static void cache_state(struct extent_state *state, | |||
690 | } | 690 | } |
691 | } | 691 | } |
692 | 692 | ||
693 | static void uncache_state(struct extent_state **cached_ptr) | ||
694 | { | ||
695 | if (cached_ptr && (*cached_ptr)) { | ||
696 | struct extent_state *state = *cached_ptr; | ||
697 | if (state->state & (EXTENT_IOBITS | EXTENT_BOUNDARY)) { | ||
698 | *cached_ptr = NULL; | ||
699 | free_extent_state(state); | ||
700 | } | ||
701 | } | ||
702 | } | ||
703 | |||
693 | /* | 704 | /* |
694 | * set some bits on a range in the tree. This may require allocations or | 705 | * set some bits on a range in the tree. This may require allocations or |
695 | * sleeping, so the gfp mask is used to indicate what is allowed. | 706 | * sleeping, so the gfp mask is used to indicate what is allowed. |
@@ -940,10 +951,10 @@ static int clear_extent_new(struct extent_io_tree *tree, u64 start, u64 end, | |||
940 | } | 951 | } |
941 | 952 | ||
942 | int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end, | 953 | int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end, |
943 | gfp_t mask) | 954 | struct extent_state **cached_state, gfp_t mask) |
944 | { | 955 | { |
945 | return set_extent_bit(tree, start, end, EXTENT_UPTODATE, 0, NULL, | 956 | return set_extent_bit(tree, start, end, EXTENT_UPTODATE, 0, |
946 | NULL, mask); | 957 | NULL, cached_state, mask); |
947 | } | 958 | } |
948 | 959 | ||
949 | static int clear_extent_uptodate(struct extent_io_tree *tree, u64 start, | 960 | static int clear_extent_uptodate(struct extent_io_tree *tree, u64 start, |
@@ -1012,8 +1023,7 @@ int unlock_extent_cached(struct extent_io_tree *tree, u64 start, u64 end, | |||
1012 | mask); | 1023 | mask); |
1013 | } | 1024 | } |
1014 | 1025 | ||
1015 | int unlock_extent(struct extent_io_tree *tree, u64 start, u64 end, | 1026 | int unlock_extent(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask) |
1016 | gfp_t mask) | ||
1017 | { | 1027 | { |
1018 | return clear_extent_bit(tree, start, end, EXTENT_LOCKED, 1, 0, NULL, | 1028 | return clear_extent_bit(tree, start, end, EXTENT_LOCKED, 1, 0, NULL, |
1019 | mask); | 1029 | mask); |
@@ -1735,6 +1745,9 @@ static void end_bio_extent_readpage(struct bio *bio, int err) | |||
1735 | 1745 | ||
1736 | do { | 1746 | do { |
1737 | struct page *page = bvec->bv_page; | 1747 | struct page *page = bvec->bv_page; |
1748 | struct extent_state *cached = NULL; | ||
1749 | struct extent_state *state; | ||
1750 | |||
1738 | tree = &BTRFS_I(page->mapping->host)->io_tree; | 1751 | tree = &BTRFS_I(page->mapping->host)->io_tree; |
1739 | 1752 | ||
1740 | start = ((u64)page->index << PAGE_CACHE_SHIFT) + | 1753 | start = ((u64)page->index << PAGE_CACHE_SHIFT) + |
@@ -1749,9 +1762,20 @@ static void end_bio_extent_readpage(struct bio *bio, int err) | |||
1749 | if (++bvec <= bvec_end) | 1762 | if (++bvec <= bvec_end) |
1750 | prefetchw(&bvec->bv_page->flags); | 1763 | prefetchw(&bvec->bv_page->flags); |
1751 | 1764 | ||
1765 | spin_lock(&tree->lock); | ||
1766 | state = find_first_extent_bit_state(tree, start, 0); | ||
1767 | if (state) { | ||
1768 | /* | ||
1769 | * take a reference on the state, unlock will drop | ||
1770 | * the ref | ||
1771 | */ | ||
1772 | cache_state(state, &cached); | ||
1773 | } | ||
1774 | spin_unlock(&tree->lock); | ||
1775 | |||
1752 | if (uptodate && tree->ops && tree->ops->readpage_end_io_hook) { | 1776 | if (uptodate && tree->ops && tree->ops->readpage_end_io_hook) { |
1753 | ret = tree->ops->readpage_end_io_hook(page, start, end, | 1777 | ret = tree->ops->readpage_end_io_hook(page, start, end, |
1754 | NULL); | 1778 | state); |
1755 | if (ret) | 1779 | if (ret) |
1756 | uptodate = 0; | 1780 | uptodate = 0; |
1757 | } | 1781 | } |
@@ -1764,15 +1788,16 @@ static void end_bio_extent_readpage(struct bio *bio, int err) | |||
1764 | test_bit(BIO_UPTODATE, &bio->bi_flags); | 1788 | test_bit(BIO_UPTODATE, &bio->bi_flags); |
1765 | if (err) | 1789 | if (err) |
1766 | uptodate = 0; | 1790 | uptodate = 0; |
1791 | uncache_state(&cached); | ||
1767 | continue; | 1792 | continue; |
1768 | } | 1793 | } |
1769 | } | 1794 | } |
1770 | 1795 | ||
1771 | if (uptodate) { | 1796 | if (uptodate) { |
1772 | set_extent_uptodate(tree, start, end, | 1797 | set_extent_uptodate(tree, start, end, &cached, |
1773 | GFP_ATOMIC); | 1798 | GFP_ATOMIC); |
1774 | } | 1799 | } |
1775 | unlock_extent(tree, start, end, GFP_ATOMIC); | 1800 | unlock_extent_cached(tree, start, end, &cached, GFP_ATOMIC); |
1776 | 1801 | ||
1777 | if (whole_page) { | 1802 | if (whole_page) { |
1778 | if (uptodate) { | 1803 | if (uptodate) { |
@@ -1811,6 +1836,7 @@ static void end_bio_extent_preparewrite(struct bio *bio, int err) | |||
1811 | 1836 | ||
1812 | do { | 1837 | do { |
1813 | struct page *page = bvec->bv_page; | 1838 | struct page *page = bvec->bv_page; |
1839 | struct extent_state *cached = NULL; | ||
1814 | tree = &BTRFS_I(page->mapping->host)->io_tree; | 1840 | tree = &BTRFS_I(page->mapping->host)->io_tree; |
1815 | 1841 | ||
1816 | start = ((u64)page->index << PAGE_CACHE_SHIFT) + | 1842 | start = ((u64)page->index << PAGE_CACHE_SHIFT) + |
@@ -1821,13 +1847,14 @@ static void end_bio_extent_preparewrite(struct bio *bio, int err) | |||
1821 | prefetchw(&bvec->bv_page->flags); | 1847 | prefetchw(&bvec->bv_page->flags); |
1822 | 1848 | ||
1823 | if (uptodate) { | 1849 | if (uptodate) { |
1824 | set_extent_uptodate(tree, start, end, GFP_ATOMIC); | 1850 | set_extent_uptodate(tree, start, end, &cached, |
1851 | GFP_ATOMIC); | ||
1825 | } else { | 1852 | } else { |
1826 | ClearPageUptodate(page); | 1853 | ClearPageUptodate(page); |
1827 | SetPageError(page); | 1854 | SetPageError(page); |
1828 | } | 1855 | } |
1829 | 1856 | ||
1830 | unlock_extent(tree, start, end, GFP_ATOMIC); | 1857 | unlock_extent_cached(tree, start, end, &cached, GFP_ATOMIC); |
1831 | 1858 | ||
1832 | } while (bvec >= bio->bi_io_vec); | 1859 | } while (bvec >= bio->bi_io_vec); |
1833 | 1860 | ||
@@ -2016,14 +2043,17 @@ static int __extent_read_full_page(struct extent_io_tree *tree, | |||
2016 | while (cur <= end) { | 2043 | while (cur <= end) { |
2017 | if (cur >= last_byte) { | 2044 | if (cur >= last_byte) { |
2018 | char *userpage; | 2045 | char *userpage; |
2046 | struct extent_state *cached = NULL; | ||
2047 | |||
2019 | iosize = PAGE_CACHE_SIZE - page_offset; | 2048 | iosize = PAGE_CACHE_SIZE - page_offset; |
2020 | userpage = kmap_atomic(page, KM_USER0); | 2049 | userpage = kmap_atomic(page, KM_USER0); |
2021 | memset(userpage + page_offset, 0, iosize); | 2050 | memset(userpage + page_offset, 0, iosize); |
2022 | flush_dcache_page(page); | 2051 | flush_dcache_page(page); |
2023 | kunmap_atomic(userpage, KM_USER0); | 2052 | kunmap_atomic(userpage, KM_USER0); |
2024 | set_extent_uptodate(tree, cur, cur + iosize - 1, | 2053 | set_extent_uptodate(tree, cur, cur + iosize - 1, |
2025 | GFP_NOFS); | 2054 | &cached, GFP_NOFS); |
2026 | unlock_extent(tree, cur, cur + iosize - 1, GFP_NOFS); | 2055 | unlock_extent_cached(tree, cur, cur + iosize - 1, |
2056 | &cached, GFP_NOFS); | ||
2027 | break; | 2057 | break; |
2028 | } | 2058 | } |
2029 | em = get_extent(inode, page, page_offset, cur, | 2059 | em = get_extent(inode, page, page_offset, cur, |
@@ -2063,14 +2093,17 @@ static int __extent_read_full_page(struct extent_io_tree *tree, | |||
2063 | /* we've found a hole, just zero and go on */ | 2093 | /* we've found a hole, just zero and go on */ |
2064 | if (block_start == EXTENT_MAP_HOLE) { | 2094 | if (block_start == EXTENT_MAP_HOLE) { |
2065 | char *userpage; | 2095 | char *userpage; |
2096 | struct extent_state *cached = NULL; | ||
2097 | |||
2066 | userpage = kmap_atomic(page, KM_USER0); | 2098 | userpage = kmap_atomic(page, KM_USER0); |
2067 | memset(userpage + page_offset, 0, iosize); | 2099 | memset(userpage + page_offset, 0, iosize); |
2068 | flush_dcache_page(page); | 2100 | flush_dcache_page(page); |
2069 | kunmap_atomic(userpage, KM_USER0); | 2101 | kunmap_atomic(userpage, KM_USER0); |
2070 | 2102 | ||
2071 | set_extent_uptodate(tree, cur, cur + iosize - 1, | 2103 | set_extent_uptodate(tree, cur, cur + iosize - 1, |
2072 | GFP_NOFS); | 2104 | &cached, GFP_NOFS); |
2073 | unlock_extent(tree, cur, cur + iosize - 1, GFP_NOFS); | 2105 | unlock_extent_cached(tree, cur, cur + iosize - 1, |
2106 | &cached, GFP_NOFS); | ||
2074 | cur = cur + iosize; | 2107 | cur = cur + iosize; |
2075 | page_offset += iosize; | 2108 | page_offset += iosize; |
2076 | continue; | 2109 | continue; |
@@ -2789,9 +2822,12 @@ int extent_prepare_write(struct extent_io_tree *tree, | |||
2789 | iocount++; | 2822 | iocount++; |
2790 | block_start = block_start + iosize; | 2823 | block_start = block_start + iosize; |
2791 | } else { | 2824 | } else { |
2792 | set_extent_uptodate(tree, block_start, cur_end, | 2825 | struct extent_state *cached = NULL; |
2826 | |||
2827 | set_extent_uptodate(tree, block_start, cur_end, &cached, | ||
2793 | GFP_NOFS); | 2828 | GFP_NOFS); |
2794 | unlock_extent(tree, block_start, cur_end, GFP_NOFS); | 2829 | unlock_extent_cached(tree, block_start, cur_end, |
2830 | &cached, GFP_NOFS); | ||
2795 | block_start = cur_end + 1; | 2831 | block_start = cur_end + 1; |
2796 | } | 2832 | } |
2797 | page_offset = block_start & (PAGE_CACHE_SIZE - 1); | 2833 | page_offset = block_start & (PAGE_CACHE_SIZE - 1); |
@@ -3457,7 +3493,7 @@ int set_extent_buffer_uptodate(struct extent_io_tree *tree, | |||
3457 | num_pages = num_extent_pages(eb->start, eb->len); | 3493 | num_pages = num_extent_pages(eb->start, eb->len); |
3458 | 3494 | ||
3459 | set_extent_uptodate(tree, eb->start, eb->start + eb->len - 1, | 3495 | set_extent_uptodate(tree, eb->start, eb->start + eb->len - 1, |
3460 | GFP_NOFS); | 3496 | NULL, GFP_NOFS); |
3461 | for (i = 0; i < num_pages; i++) { | 3497 | for (i = 0; i < num_pages; i++) { |
3462 | page = extent_buffer_page(eb, i); | 3498 | page = extent_buffer_page(eb, i); |
3463 | if ((i == 0 && (eb->start & (PAGE_CACHE_SIZE - 1))) || | 3499 | if ((i == 0 && (eb->start & (PAGE_CACHE_SIZE - 1))) || |
@@ -3885,6 +3921,12 @@ static void move_pages(struct page *dst_page, struct page *src_page, | |||
3885 | kunmap_atomic(dst_kaddr, KM_USER0); | 3921 | kunmap_atomic(dst_kaddr, KM_USER0); |
3886 | } | 3922 | } |
3887 | 3923 | ||
3924 | static inline bool areas_overlap(unsigned long src, unsigned long dst, unsigned long len) | ||
3925 | { | ||
3926 | unsigned long distance = (src > dst) ? src - dst : dst - src; | ||
3927 | return distance < len; | ||
3928 | } | ||
3929 | |||
3888 | static void copy_pages(struct page *dst_page, struct page *src_page, | 3930 | static void copy_pages(struct page *dst_page, struct page *src_page, |
3889 | unsigned long dst_off, unsigned long src_off, | 3931 | unsigned long dst_off, unsigned long src_off, |
3890 | unsigned long len) | 3932 | unsigned long len) |
@@ -3892,10 +3934,12 @@ static void copy_pages(struct page *dst_page, struct page *src_page, | |||
3892 | char *dst_kaddr = kmap_atomic(dst_page, KM_USER0); | 3934 | char *dst_kaddr = kmap_atomic(dst_page, KM_USER0); |
3893 | char *src_kaddr; | 3935 | char *src_kaddr; |
3894 | 3936 | ||
3895 | if (dst_page != src_page) | 3937 | if (dst_page != src_page) { |
3896 | src_kaddr = kmap_atomic(src_page, KM_USER1); | 3938 | src_kaddr = kmap_atomic(src_page, KM_USER1); |
3897 | else | 3939 | } else { |
3898 | src_kaddr = dst_kaddr; | 3940 | src_kaddr = dst_kaddr; |
3941 | BUG_ON(areas_overlap(src_off, dst_off, len)); | ||
3942 | } | ||
3899 | 3943 | ||
3900 | memcpy(dst_kaddr + dst_off, src_kaddr + src_off, len); | 3944 | memcpy(dst_kaddr + dst_off, src_kaddr + src_off, len); |
3901 | kunmap_atomic(dst_kaddr, KM_USER0); | 3945 | kunmap_atomic(dst_kaddr, KM_USER0); |
@@ -3970,7 +4014,7 @@ void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset, | |||
3970 | "len %lu len %lu\n", dst_offset, len, dst->len); | 4014 | "len %lu len %lu\n", dst_offset, len, dst->len); |
3971 | BUG_ON(1); | 4015 | BUG_ON(1); |
3972 | } | 4016 | } |
3973 | if (dst_offset < src_offset) { | 4017 | if (!areas_overlap(src_offset, dst_offset, len)) { |
3974 | memcpy_extent_buffer(dst, dst_offset, src_offset, len); | 4018 | memcpy_extent_buffer(dst, dst_offset, src_offset, len); |
3975 | return; | 4019 | return; |
3976 | } | 4020 | } |
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index f62c5442835d..af2d7179c372 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h | |||
@@ -208,7 +208,7 @@ int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, | |||
208 | int bits, int exclusive_bits, u64 *failed_start, | 208 | int bits, int exclusive_bits, u64 *failed_start, |
209 | struct extent_state **cached_state, gfp_t mask); | 209 | struct extent_state **cached_state, gfp_t mask); |
210 | int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end, | 210 | int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end, |
211 | gfp_t mask); | 211 | struct extent_state **cached_state, gfp_t mask); |
212 | int set_extent_new(struct extent_io_tree *tree, u64 start, u64 end, | 212 | int set_extent_new(struct extent_io_tree *tree, u64 start, u64 end, |
213 | gfp_t mask); | 213 | gfp_t mask); |
214 | int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, | 214 | int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, |
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 00d59c6a9769..55a6a0b416d7 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -4541,14 +4541,17 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans, | |||
4541 | BUG_ON(!path); | 4541 | BUG_ON(!path); |
4542 | 4542 | ||
4543 | inode = new_inode(root->fs_info->sb); | 4543 | inode = new_inode(root->fs_info->sb); |
4544 | if (!inode) | 4544 | if (!inode) { |
4545 | btrfs_free_path(path); | ||
4545 | return ERR_PTR(-ENOMEM); | 4546 | return ERR_PTR(-ENOMEM); |
4547 | } | ||
4546 | 4548 | ||
4547 | if (dir) { | 4549 | if (dir) { |
4548 | trace_btrfs_inode_request(dir); | 4550 | trace_btrfs_inode_request(dir); |
4549 | 4551 | ||
4550 | ret = btrfs_set_inode_index(dir, index); | 4552 | ret = btrfs_set_inode_index(dir, index); |
4551 | if (ret) { | 4553 | if (ret) { |
4554 | btrfs_free_path(path); | ||
4552 | iput(inode); | 4555 | iput(inode); |
4553 | return ERR_PTR(ret); | 4556 | return ERR_PTR(ret); |
4554 | } | 4557 | } |
@@ -5238,7 +5241,7 @@ again: | |||
5238 | btrfs_mark_buffer_dirty(leaf); | 5241 | btrfs_mark_buffer_dirty(leaf); |
5239 | } | 5242 | } |
5240 | set_extent_uptodate(io_tree, em->start, | 5243 | set_extent_uptodate(io_tree, em->start, |
5241 | extent_map_end(em) - 1, GFP_NOFS); | 5244 | extent_map_end(em) - 1, NULL, GFP_NOFS); |
5242 | goto insert; | 5245 | goto insert; |
5243 | } else { | 5246 | } else { |
5244 | printk(KERN_ERR "btrfs unknown found_type %d\n", found_type); | 5247 | printk(KERN_ERR "btrfs unknown found_type %d\n", found_type); |
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f9c93a9ed4a7..f580a3a5d2fc 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -2287,7 +2287,7 @@ long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg) | |||
2287 | struct btrfs_ioctl_space_info space; | 2287 | struct btrfs_ioctl_space_info space; |
2288 | struct btrfs_ioctl_space_info *dest; | 2288 | struct btrfs_ioctl_space_info *dest; |
2289 | struct btrfs_ioctl_space_info *dest_orig; | 2289 | struct btrfs_ioctl_space_info *dest_orig; |
2290 | struct btrfs_ioctl_space_info *user_dest; | 2290 | struct btrfs_ioctl_space_info __user *user_dest; |
2291 | struct btrfs_space_info *info; | 2291 | struct btrfs_space_info *info; |
2292 | u64 types[] = {BTRFS_BLOCK_GROUP_DATA, | 2292 | u64 types[] = {BTRFS_BLOCK_GROUP_DATA, |
2293 | BTRFS_BLOCK_GROUP_SYSTEM, | 2293 | BTRFS_BLOCK_GROUP_SYSTEM, |
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 58e7de9cc90c..0ac712efcdf2 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c | |||
@@ -159,7 +159,7 @@ enum { | |||
159 | Opt_compress_type, Opt_compress_force, Opt_compress_force_type, | 159 | Opt_compress_type, Opt_compress_force, Opt_compress_force_type, |
160 | Opt_notreelog, Opt_ratio, Opt_flushoncommit, Opt_discard, | 160 | Opt_notreelog, Opt_ratio, Opt_flushoncommit, Opt_discard, |
161 | Opt_space_cache, Opt_clear_cache, Opt_user_subvol_rm_allowed, | 161 | Opt_space_cache, Opt_clear_cache, Opt_user_subvol_rm_allowed, |
162 | Opt_enospc_debug, Opt_err, | 162 | Opt_enospc_debug, Opt_subvolrootid, Opt_err, |
163 | }; | 163 | }; |
164 | 164 | ||
165 | static match_table_t tokens = { | 165 | static match_table_t tokens = { |
@@ -189,6 +189,7 @@ static match_table_t tokens = { | |||
189 | {Opt_clear_cache, "clear_cache"}, | 189 | {Opt_clear_cache, "clear_cache"}, |
190 | {Opt_user_subvol_rm_allowed, "user_subvol_rm_allowed"}, | 190 | {Opt_user_subvol_rm_allowed, "user_subvol_rm_allowed"}, |
191 | {Opt_enospc_debug, "enospc_debug"}, | 191 | {Opt_enospc_debug, "enospc_debug"}, |
192 | {Opt_subvolrootid, "subvolrootid=%d"}, | ||
192 | {Opt_err, NULL}, | 193 | {Opt_err, NULL}, |
193 | }; | 194 | }; |
194 | 195 | ||
@@ -232,6 +233,7 @@ int btrfs_parse_options(struct btrfs_root *root, char *options) | |||
232 | break; | 233 | break; |
233 | case Opt_subvol: | 234 | case Opt_subvol: |
234 | case Opt_subvolid: | 235 | case Opt_subvolid: |
236 | case Opt_subvolrootid: | ||
235 | case Opt_device: | 237 | case Opt_device: |
236 | /* | 238 | /* |
237 | * These are parsed by btrfs_parse_early_options | 239 | * These are parsed by btrfs_parse_early_options |
@@ -388,7 +390,7 @@ out: | |||
388 | */ | 390 | */ |
389 | static int btrfs_parse_early_options(const char *options, fmode_t flags, | 391 | static int btrfs_parse_early_options(const char *options, fmode_t flags, |
390 | void *holder, char **subvol_name, u64 *subvol_objectid, | 392 | void *holder, char **subvol_name, u64 *subvol_objectid, |
391 | struct btrfs_fs_devices **fs_devices) | 393 | u64 *subvol_rootid, struct btrfs_fs_devices **fs_devices) |
392 | { | 394 | { |
393 | substring_t args[MAX_OPT_ARGS]; | 395 | substring_t args[MAX_OPT_ARGS]; |
394 | char *opts, *orig, *p; | 396 | char *opts, *orig, *p; |
@@ -429,6 +431,18 @@ static int btrfs_parse_early_options(const char *options, fmode_t flags, | |||
429 | *subvol_objectid = intarg; | 431 | *subvol_objectid = intarg; |
430 | } | 432 | } |
431 | break; | 433 | break; |
434 | case Opt_subvolrootid: | ||
435 | intarg = 0; | ||
436 | error = match_int(&args[0], &intarg); | ||
437 | if (!error) { | ||
438 | /* we want the original fs_tree */ | ||
439 | if (!intarg) | ||
440 | *subvol_rootid = | ||
441 | BTRFS_FS_TREE_OBJECTID; | ||
442 | else | ||
443 | *subvol_rootid = intarg; | ||
444 | } | ||
445 | break; | ||
432 | case Opt_device: | 446 | case Opt_device: |
433 | error = btrfs_scan_one_device(match_strdup(&args[0]), | 447 | error = btrfs_scan_one_device(match_strdup(&args[0]), |
434 | flags, holder, fs_devices); | 448 | flags, holder, fs_devices); |
@@ -736,6 +750,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, | |||
736 | fmode_t mode = FMODE_READ; | 750 | fmode_t mode = FMODE_READ; |
737 | char *subvol_name = NULL; | 751 | char *subvol_name = NULL; |
738 | u64 subvol_objectid = 0; | 752 | u64 subvol_objectid = 0; |
753 | u64 subvol_rootid = 0; | ||
739 | int error = 0; | 754 | int error = 0; |
740 | 755 | ||
741 | if (!(flags & MS_RDONLY)) | 756 | if (!(flags & MS_RDONLY)) |
@@ -743,7 +758,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, | |||
743 | 758 | ||
744 | error = btrfs_parse_early_options(data, mode, fs_type, | 759 | error = btrfs_parse_early_options(data, mode, fs_type, |
745 | &subvol_name, &subvol_objectid, | 760 | &subvol_name, &subvol_objectid, |
746 | &fs_devices); | 761 | &subvol_rootid, &fs_devices); |
747 | if (error) | 762 | if (error) |
748 | return ERR_PTR(error); | 763 | return ERR_PTR(error); |
749 | 764 | ||
@@ -807,15 +822,17 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, | |||
807 | s->s_flags |= MS_ACTIVE; | 822 | s->s_flags |= MS_ACTIVE; |
808 | } | 823 | } |
809 | 824 | ||
810 | root = get_default_root(s, subvol_objectid); | ||
811 | if (IS_ERR(root)) { | ||
812 | error = PTR_ERR(root); | ||
813 | deactivate_locked_super(s); | ||
814 | goto error_free_subvol_name; | ||
815 | } | ||
816 | /* if they gave us a subvolume name bind mount into that */ | 825 | /* if they gave us a subvolume name bind mount into that */ |
817 | if (strcmp(subvol_name, ".")) { | 826 | if (strcmp(subvol_name, ".")) { |
818 | struct dentry *new_root; | 827 | struct dentry *new_root; |
828 | |||
829 | root = get_default_root(s, subvol_rootid); | ||
830 | if (IS_ERR(root)) { | ||
831 | error = PTR_ERR(root); | ||
832 | deactivate_locked_super(s); | ||
833 | goto error_free_subvol_name; | ||
834 | } | ||
835 | |||
819 | mutex_lock(&root->d_inode->i_mutex); | 836 | mutex_lock(&root->d_inode->i_mutex); |
820 | new_root = lookup_one_len(subvol_name, root, | 837 | new_root = lookup_one_len(subvol_name, root, |
821 | strlen(subvol_name)); | 838 | strlen(subvol_name)); |
@@ -836,6 +853,13 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, | |||
836 | } | 853 | } |
837 | dput(root); | 854 | dput(root); |
838 | root = new_root; | 855 | root = new_root; |
856 | } else { | ||
857 | root = get_default_root(s, subvol_objectid); | ||
858 | if (IS_ERR(root)) { | ||
859 | error = PTR_ERR(root); | ||
860 | deactivate_locked_super(s); | ||
861 | goto error_free_subvol_name; | ||
862 | } | ||
839 | } | 863 | } |
840 | 864 | ||
841 | kfree(subvol_name); | 865 | kfree(subvol_name); |