.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* -*- mode: c; c-basic-offset: 8; -*- |
---|
2 | 3 | * vim: noexpandtab sw=8 ts=8 sts=0: |
---|
3 | 4 | * |
---|
4 | 5 | * refcounttree.c |
---|
5 | 6 | * |
---|
6 | 7 | * Copyright (C) 2009 Oracle. All rights reserved. |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or |
---|
9 | | - * modify it under the terms of the GNU General Public |
---|
10 | | - * License version 2 as published by the Free Software Foundation. |
---|
11 | | - * |
---|
12 | | - * This program is distributed in the hope that it will be useful, |
---|
13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
15 | | - * General Public License for more details. |
---|
16 | 8 | */ |
---|
17 | 9 | |
---|
18 | 10 | #include <linux/sort.h> |
---|
.. | .. |
---|
162 | 154 | } |
---|
163 | 155 | |
---|
164 | 156 | static void ocfs2_refcount_cache_lock(struct ocfs2_caching_info *ci) |
---|
| 157 | +__acquires(&rf->rf_lock) |
---|
165 | 158 | { |
---|
166 | 159 | struct ocfs2_refcount_tree *rf = cache_info_to_refcount(ci); |
---|
167 | 160 | |
---|
.. | .. |
---|
169 | 162 | } |
---|
170 | 163 | |
---|
171 | 164 | static void ocfs2_refcount_cache_unlock(struct ocfs2_caching_info *ci) |
---|
| 165 | +__releases(&rf->rf_lock) |
---|
172 | 166 | { |
---|
173 | 167 | struct ocfs2_refcount_tree *rf = cache_info_to_refcount(ci); |
---|
174 | 168 | |
---|
.. | .. |
---|
1069 | 1063 | struct buffer_head **ret_bh) |
---|
1070 | 1064 | { |
---|
1071 | 1065 | int ret = 0, i, found; |
---|
1072 | | - u32 low_cpos, uninitialized_var(cpos_end); |
---|
| 1066 | + u32 low_cpos, cpos_end; |
---|
1073 | 1067 | struct ocfs2_extent_list *el; |
---|
1074 | 1068 | struct ocfs2_extent_rec *rec = NULL; |
---|
1075 | 1069 | struct ocfs2_extent_block *eb = NULL; |
---|
.. | .. |
---|
4135 | 4129 | struct buffer_head *ref_root_bh = NULL; |
---|
4136 | 4130 | struct ocfs2_cached_dealloc_ctxt dealloc; |
---|
4137 | 4131 | struct ocfs2_super *osb = OCFS2_SB(s_inode->i_sb); |
---|
4138 | | - struct ocfs2_refcount_block *rb; |
---|
4139 | 4132 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)s_bh->b_data; |
---|
4140 | 4133 | struct ocfs2_refcount_tree *ref_tree; |
---|
4141 | 4134 | |
---|
.. | .. |
---|
4162 | 4155 | mlog_errno(ret); |
---|
4163 | 4156 | goto out; |
---|
4164 | 4157 | } |
---|
4165 | | - rb = (struct ocfs2_refcount_block *)ref_root_bh->b_data; |
---|
4166 | 4158 | |
---|
4167 | 4159 | ret = ocfs2_duplicate_extent_list(s_inode, t_inode, t_bh, |
---|
4168 | 4160 | &ref_tree->rf_ci, ref_root_bh, |
---|
.. | .. |
---|
4468 | 4460 | } |
---|
4469 | 4461 | |
---|
4470 | 4462 | /* Update destination inode size, if necessary. */ |
---|
4471 | | -static int ocfs2_reflink_update_dest(struct inode *dest, |
---|
4472 | | - struct buffer_head *d_bh, |
---|
4473 | | - loff_t newlen) |
---|
| 4463 | +int ocfs2_reflink_update_dest(struct inode *dest, |
---|
| 4464 | + struct buffer_head *d_bh, |
---|
| 4465 | + loff_t newlen) |
---|
4474 | 4466 | { |
---|
4475 | 4467 | handle_t *handle; |
---|
4476 | 4468 | int ret; |
---|
.. | .. |
---|
4507 | 4499 | } |
---|
4508 | 4500 | |
---|
4509 | 4501 | /* Remap the range pos_in:len in s_inode to pos_out:len in t_inode. */ |
---|
4510 | | -static int ocfs2_reflink_remap_extent(struct inode *s_inode, |
---|
4511 | | - struct buffer_head *s_bh, |
---|
4512 | | - loff_t pos_in, |
---|
4513 | | - struct inode *t_inode, |
---|
4514 | | - struct buffer_head *t_bh, |
---|
4515 | | - loff_t pos_out, |
---|
4516 | | - loff_t len, |
---|
4517 | | - struct ocfs2_cached_dealloc_ctxt *dealloc) |
---|
| 4502 | +static loff_t ocfs2_reflink_remap_extent(struct inode *s_inode, |
---|
| 4503 | + struct buffer_head *s_bh, |
---|
| 4504 | + loff_t pos_in, |
---|
| 4505 | + struct inode *t_inode, |
---|
| 4506 | + struct buffer_head *t_bh, |
---|
| 4507 | + loff_t pos_out, |
---|
| 4508 | + loff_t len, |
---|
| 4509 | + struct ocfs2_cached_dealloc_ctxt *dealloc) |
---|
4518 | 4510 | { |
---|
4519 | 4511 | struct ocfs2_extent_tree s_et; |
---|
4520 | 4512 | struct ocfs2_extent_tree t_et; |
---|
.. | .. |
---|
4522 | 4514 | struct buffer_head *ref_root_bh = NULL; |
---|
4523 | 4515 | struct ocfs2_refcount_tree *ref_tree; |
---|
4524 | 4516 | struct ocfs2_super *osb; |
---|
| 4517 | + loff_t remapped_bytes = 0; |
---|
4525 | 4518 | loff_t pstart, plen; |
---|
4526 | | - u32 p_cluster, num_clusters, slast, spos, tpos; |
---|
| 4519 | + u32 p_cluster, num_clusters, slast, spos, tpos, remapped_clus = 0; |
---|
4527 | 4520 | unsigned int ext_flags; |
---|
4528 | 4521 | int ret = 0; |
---|
4529 | 4522 | |
---|
.. | .. |
---|
4605 | 4598 | next_loop: |
---|
4606 | 4599 | spos += num_clusters; |
---|
4607 | 4600 | tpos += num_clusters; |
---|
| 4601 | + remapped_clus += num_clusters; |
---|
4608 | 4602 | } |
---|
4609 | 4603 | |
---|
4610 | | -out: |
---|
4611 | | - return ret; |
---|
| 4604 | + goto out; |
---|
4612 | 4605 | out_unlock_refcount: |
---|
4613 | 4606 | ocfs2_unlock_refcount_tree(osb, ref_tree, 1); |
---|
4614 | 4607 | brelse(ref_root_bh); |
---|
4615 | | - return ret; |
---|
| 4608 | +out: |
---|
| 4609 | + remapped_bytes = ocfs2_clusters_to_bytes(t_inode->i_sb, remapped_clus); |
---|
| 4610 | + remapped_bytes = min_t(loff_t, len, remapped_bytes); |
---|
| 4611 | + |
---|
| 4612 | + return remapped_bytes > 0 ? remapped_bytes : ret; |
---|
4616 | 4613 | } |
---|
4617 | 4614 | |
---|
4618 | 4615 | /* Set up refcount tree and remap s_inode to t_inode. */ |
---|
4619 | | -static int ocfs2_reflink_remap_blocks(struct inode *s_inode, |
---|
4620 | | - struct buffer_head *s_bh, |
---|
4621 | | - loff_t pos_in, |
---|
4622 | | - struct inode *t_inode, |
---|
4623 | | - struct buffer_head *t_bh, |
---|
4624 | | - loff_t pos_out, |
---|
4625 | | - loff_t len) |
---|
| 4616 | +loff_t ocfs2_reflink_remap_blocks(struct inode *s_inode, |
---|
| 4617 | + struct buffer_head *s_bh, |
---|
| 4618 | + loff_t pos_in, |
---|
| 4619 | + struct inode *t_inode, |
---|
| 4620 | + struct buffer_head *t_bh, |
---|
| 4621 | + loff_t pos_out, |
---|
| 4622 | + loff_t len) |
---|
4626 | 4623 | { |
---|
4627 | 4624 | struct ocfs2_cached_dealloc_ctxt dealloc; |
---|
4628 | 4625 | struct ocfs2_super *osb; |
---|
4629 | 4626 | struct ocfs2_dinode *dis; |
---|
4630 | 4627 | struct ocfs2_dinode *dit; |
---|
4631 | | - int ret; |
---|
| 4628 | + loff_t ret; |
---|
4632 | 4629 | |
---|
4633 | 4630 | osb = OCFS2_SB(s_inode->i_sb); |
---|
4634 | 4631 | dis = (struct ocfs2_dinode *)s_bh->b_data; |
---|
.. | .. |
---|
4700 | 4697 | /* Actually remap extents now. */ |
---|
4701 | 4698 | ret = ocfs2_reflink_remap_extent(s_inode, s_bh, pos_in, t_inode, t_bh, |
---|
4702 | 4699 | pos_out, len, &dealloc); |
---|
4703 | | - if (ret) { |
---|
| 4700 | + if (ret < 0) { |
---|
4704 | 4701 | mlog_errno(ret); |
---|
4705 | 4702 | goto out; |
---|
4706 | 4703 | } |
---|
.. | .. |
---|
4715 | 4712 | } |
---|
4716 | 4713 | |
---|
4717 | 4714 | /* Lock an inode and grab a bh pointing to the inode. */ |
---|
4718 | | -static int ocfs2_reflink_inodes_lock(struct inode *s_inode, |
---|
4719 | | - struct buffer_head **bh_s, |
---|
4720 | | - struct inode *t_inode, |
---|
4721 | | - struct buffer_head **bh_t) |
---|
| 4715 | +int ocfs2_reflink_inodes_lock(struct inode *s_inode, |
---|
| 4716 | + struct buffer_head **bh_s, |
---|
| 4717 | + struct inode *t_inode, |
---|
| 4718 | + struct buffer_head **bh_t) |
---|
4722 | 4719 | { |
---|
4723 | 4720 | struct inode *inode1 = s_inode; |
---|
4724 | 4721 | struct inode *inode2 = t_inode; |
---|
.. | .. |
---|
4809 | 4806 | } |
---|
4810 | 4807 | |
---|
4811 | 4808 | /* Unlock both inodes and release buffers. */ |
---|
4812 | | -static void ocfs2_reflink_inodes_unlock(struct inode *s_inode, |
---|
4813 | | - struct buffer_head *s_bh, |
---|
4814 | | - struct inode *t_inode, |
---|
4815 | | - struct buffer_head *t_bh) |
---|
| 4809 | +void ocfs2_reflink_inodes_unlock(struct inode *s_inode, |
---|
| 4810 | + struct buffer_head *s_bh, |
---|
| 4811 | + struct inode *t_inode, |
---|
| 4812 | + struct buffer_head *t_bh) |
---|
4816 | 4813 | { |
---|
4817 | 4814 | ocfs2_inode_unlock(s_inode, 1); |
---|
4818 | 4815 | ocfs2_rw_unlock(s_inode, 1); |
---|
.. | .. |
---|
4823 | 4820 | brelse(t_bh); |
---|
4824 | 4821 | } |
---|
4825 | 4822 | unlock_two_nondirectories(s_inode, t_inode); |
---|
4826 | | -} |
---|
4827 | | - |
---|
4828 | | -/* Link a range of blocks from one file to another. */ |
---|
4829 | | -int ocfs2_reflink_remap_range(struct file *file_in, |
---|
4830 | | - loff_t pos_in, |
---|
4831 | | - struct file *file_out, |
---|
4832 | | - loff_t pos_out, |
---|
4833 | | - u64 len, |
---|
4834 | | - bool is_dedupe) |
---|
4835 | | -{ |
---|
4836 | | - struct inode *inode_in = file_inode(file_in); |
---|
4837 | | - struct inode *inode_out = file_inode(file_out); |
---|
4838 | | - struct ocfs2_super *osb = OCFS2_SB(inode_in->i_sb); |
---|
4839 | | - struct buffer_head *in_bh = NULL, *out_bh = NULL; |
---|
4840 | | - bool same_inode = (inode_in == inode_out); |
---|
4841 | | - ssize_t ret; |
---|
4842 | | - |
---|
4843 | | - if (!ocfs2_refcount_tree(osb)) |
---|
4844 | | - return -EOPNOTSUPP; |
---|
4845 | | - if (ocfs2_is_hard_readonly(osb) || ocfs2_is_soft_readonly(osb)) |
---|
4846 | | - return -EROFS; |
---|
4847 | | - |
---|
4848 | | - /* Lock both files against IO */ |
---|
4849 | | - ret = ocfs2_reflink_inodes_lock(inode_in, &in_bh, inode_out, &out_bh); |
---|
4850 | | - if (ret) |
---|
4851 | | - return ret; |
---|
4852 | | - |
---|
4853 | | - /* Check file eligibility and prepare for block sharing. */ |
---|
4854 | | - ret = -EINVAL; |
---|
4855 | | - if ((OCFS2_I(inode_in)->ip_flags & OCFS2_INODE_SYSTEM_FILE) || |
---|
4856 | | - (OCFS2_I(inode_out)->ip_flags & OCFS2_INODE_SYSTEM_FILE)) |
---|
4857 | | - goto out_unlock; |
---|
4858 | | - |
---|
4859 | | - ret = vfs_clone_file_prep_inodes(inode_in, pos_in, inode_out, pos_out, |
---|
4860 | | - &len, is_dedupe); |
---|
4861 | | - if (ret <= 0) |
---|
4862 | | - goto out_unlock; |
---|
4863 | | - |
---|
4864 | | - /* Lock out changes to the allocation maps and remap. */ |
---|
4865 | | - down_write(&OCFS2_I(inode_in)->ip_alloc_sem); |
---|
4866 | | - if (!same_inode) |
---|
4867 | | - down_write_nested(&OCFS2_I(inode_out)->ip_alloc_sem, |
---|
4868 | | - SINGLE_DEPTH_NESTING); |
---|
4869 | | - |
---|
4870 | | - ret = ocfs2_reflink_remap_blocks(inode_in, in_bh, pos_in, inode_out, |
---|
4871 | | - out_bh, pos_out, len); |
---|
4872 | | - |
---|
4873 | | - /* Zap any page cache for the destination file's range. */ |
---|
4874 | | - if (!ret) |
---|
4875 | | - truncate_inode_pages_range(&inode_out->i_data, pos_out, |
---|
4876 | | - PAGE_ALIGN(pos_out + len) - 1); |
---|
4877 | | - |
---|
4878 | | - up_write(&OCFS2_I(inode_in)->ip_alloc_sem); |
---|
4879 | | - if (!same_inode) |
---|
4880 | | - up_write(&OCFS2_I(inode_out)->ip_alloc_sem); |
---|
4881 | | - if (ret) { |
---|
4882 | | - mlog_errno(ret); |
---|
4883 | | - goto out_unlock; |
---|
4884 | | - } |
---|
4885 | | - |
---|
4886 | | - /* |
---|
4887 | | - * Empty the extent map so that we may get the right extent |
---|
4888 | | - * record from the disk. |
---|
4889 | | - */ |
---|
4890 | | - ocfs2_extent_map_trunc(inode_in, 0); |
---|
4891 | | - ocfs2_extent_map_trunc(inode_out, 0); |
---|
4892 | | - |
---|
4893 | | - ret = ocfs2_reflink_update_dest(inode_out, out_bh, pos_out + len); |
---|
4894 | | - if (ret) { |
---|
4895 | | - mlog_errno(ret); |
---|
4896 | | - goto out_unlock; |
---|
4897 | | - } |
---|
4898 | | - |
---|
4899 | | - ocfs2_reflink_inodes_unlock(inode_in, in_bh, inode_out, out_bh); |
---|
4900 | | - return 0; |
---|
4901 | | - |
---|
4902 | | -out_unlock: |
---|
4903 | | - ocfs2_reflink_inodes_unlock(inode_in, in_bh, inode_out, out_bh); |
---|
4904 | | - return ret; |
---|
4905 | 4823 | } |
---|