| .. | .. |
|---|
| 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 | } |
|---|