18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2008,2009 NEC Software Tohoku, Ltd. 48c2ecf20Sopenharmony_ci * Written by Takashi Sato <t-sato@yk.jp.nec.com> 58c2ecf20Sopenharmony_ci * Akira Fujita <a-fujita@rs.jp.nec.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/fs.h> 98c2ecf20Sopenharmony_ci#include <linux/quotaops.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include "ext4_jbd2.h" 128c2ecf20Sopenharmony_ci#include "ext4.h" 138c2ecf20Sopenharmony_ci#include "ext4_extents.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/** 168c2ecf20Sopenharmony_ci * get_ext_path() - Find an extent path for designated logical block number. 178c2ecf20Sopenharmony_ci * @inode: inode to be searched 188c2ecf20Sopenharmony_ci * @lblock: logical block number to find an extent path 198c2ecf20Sopenharmony_ci * @ppath: pointer to an extent path pointer (for output) 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * ext4_find_extent wrapper. Return 0 on success, or a negative error value 228c2ecf20Sopenharmony_ci * on failure. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_cistatic inline int 258c2ecf20Sopenharmony_ciget_ext_path(struct inode *inode, ext4_lblk_t lblock, 268c2ecf20Sopenharmony_ci struct ext4_ext_path **ppath) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct ext4_ext_path *path; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci path = ext4_find_extent(inode, lblock, ppath, EXT4_EX_NOCACHE); 318c2ecf20Sopenharmony_ci if (IS_ERR(path)) 328c2ecf20Sopenharmony_ci return PTR_ERR(path); 338c2ecf20Sopenharmony_ci if (path[ext_depth(inode)].p_ext == NULL) { 348c2ecf20Sopenharmony_ci ext4_ext_drop_refs(path); 358c2ecf20Sopenharmony_ci kfree(path); 368c2ecf20Sopenharmony_ci *ppath = NULL; 378c2ecf20Sopenharmony_ci return -ENODATA; 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci *ppath = path; 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/** 448c2ecf20Sopenharmony_ci * ext4_double_down_write_data_sem() - write lock two inodes's i_data_sem 458c2ecf20Sopenharmony_ci * @first: inode to be locked 468c2ecf20Sopenharmony_ci * @second: inode to be locked 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * Acquire write lock of i_data_sem of the two inodes 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_civoid 518c2ecf20Sopenharmony_ciext4_double_down_write_data_sem(struct inode *first, struct inode *second) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci if (first < second) { 548c2ecf20Sopenharmony_ci down_write(&EXT4_I(first)->i_data_sem); 558c2ecf20Sopenharmony_ci down_write_nested(&EXT4_I(second)->i_data_sem, I_DATA_SEM_OTHER); 568c2ecf20Sopenharmony_ci } else { 578c2ecf20Sopenharmony_ci down_write(&EXT4_I(second)->i_data_sem); 588c2ecf20Sopenharmony_ci down_write_nested(&EXT4_I(first)->i_data_sem, I_DATA_SEM_OTHER); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/** 648c2ecf20Sopenharmony_ci * ext4_double_up_write_data_sem - Release two inodes' write lock of i_data_sem 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * @orig_inode: original inode structure to be released its lock first 678c2ecf20Sopenharmony_ci * @donor_inode: donor inode structure to be released its lock second 688c2ecf20Sopenharmony_ci * Release write lock of i_data_sem of two inodes (orig and donor). 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_civoid 718c2ecf20Sopenharmony_ciext4_double_up_write_data_sem(struct inode *orig_inode, 728c2ecf20Sopenharmony_ci struct inode *donor_inode) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci up_write(&EXT4_I(orig_inode)->i_data_sem); 758c2ecf20Sopenharmony_ci up_write(&EXT4_I(donor_inode)->i_data_sem); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/** 798c2ecf20Sopenharmony_ci * mext_check_coverage - Check that all extents in range has the same type 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * @inode: inode in question 828c2ecf20Sopenharmony_ci * @from: block offset of inode 838c2ecf20Sopenharmony_ci * @count: block count to be checked 848c2ecf20Sopenharmony_ci * @unwritten: extents expected to be unwritten 858c2ecf20Sopenharmony_ci * @err: pointer to save error value 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * Return 1 if all extents in range has expected type, and zero otherwise. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic int 908c2ecf20Sopenharmony_cimext_check_coverage(struct inode *inode, ext4_lblk_t from, ext4_lblk_t count, 918c2ecf20Sopenharmony_ci int unwritten, int *err) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct ext4_ext_path *path = NULL; 948c2ecf20Sopenharmony_ci struct ext4_extent *ext; 958c2ecf20Sopenharmony_ci int ret = 0; 968c2ecf20Sopenharmony_ci ext4_lblk_t last = from + count; 978c2ecf20Sopenharmony_ci while (from < last) { 988c2ecf20Sopenharmony_ci *err = get_ext_path(inode, from, &path); 998c2ecf20Sopenharmony_ci if (*err) 1008c2ecf20Sopenharmony_ci goto out; 1018c2ecf20Sopenharmony_ci ext = path[ext_depth(inode)].p_ext; 1028c2ecf20Sopenharmony_ci if (unwritten != ext4_ext_is_unwritten(ext)) 1038c2ecf20Sopenharmony_ci goto out; 1048c2ecf20Sopenharmony_ci from += ext4_ext_get_actual_len(ext); 1058c2ecf20Sopenharmony_ci ext4_ext_drop_refs(path); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci ret = 1; 1088c2ecf20Sopenharmony_ciout: 1098c2ecf20Sopenharmony_ci ext4_ext_drop_refs(path); 1108c2ecf20Sopenharmony_ci kfree(path); 1118c2ecf20Sopenharmony_ci return ret; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/** 1158c2ecf20Sopenharmony_ci * mext_page_double_lock - Grab and lock pages on both @inode1 and @inode2 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * @inode1: the inode structure 1188c2ecf20Sopenharmony_ci * @inode2: the inode structure 1198c2ecf20Sopenharmony_ci * @index1: page index 1208c2ecf20Sopenharmony_ci * @index2: page index 1218c2ecf20Sopenharmony_ci * @page: result page vector 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * Grab two locked pages for inode's by inode order 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistatic int 1268c2ecf20Sopenharmony_cimext_page_double_lock(struct inode *inode1, struct inode *inode2, 1278c2ecf20Sopenharmony_ci pgoff_t index1, pgoff_t index2, struct page *page[2]) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct address_space *mapping[2]; 1308c2ecf20Sopenharmony_ci unsigned fl = AOP_FLAG_NOFS; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci BUG_ON(!inode1 || !inode2); 1338c2ecf20Sopenharmony_ci if (inode1 < inode2) { 1348c2ecf20Sopenharmony_ci mapping[0] = inode1->i_mapping; 1358c2ecf20Sopenharmony_ci mapping[1] = inode2->i_mapping; 1368c2ecf20Sopenharmony_ci } else { 1378c2ecf20Sopenharmony_ci swap(index1, index2); 1388c2ecf20Sopenharmony_ci mapping[0] = inode2->i_mapping; 1398c2ecf20Sopenharmony_ci mapping[1] = inode1->i_mapping; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci page[0] = grab_cache_page_write_begin(mapping[0], index1, fl); 1438c2ecf20Sopenharmony_ci if (!page[0]) 1448c2ecf20Sopenharmony_ci return -ENOMEM; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci page[1] = grab_cache_page_write_begin(mapping[1], index2, fl); 1478c2ecf20Sopenharmony_ci if (!page[1]) { 1488c2ecf20Sopenharmony_ci unlock_page(page[0]); 1498c2ecf20Sopenharmony_ci put_page(page[0]); 1508c2ecf20Sopenharmony_ci return -ENOMEM; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * grab_cache_page_write_begin() may not wait on page's writeback if 1548c2ecf20Sopenharmony_ci * BDI not demand that. But it is reasonable to be very conservative 1558c2ecf20Sopenharmony_ci * here and explicitly wait on page's writeback 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci wait_on_page_writeback(page[0]); 1588c2ecf20Sopenharmony_ci wait_on_page_writeback(page[1]); 1598c2ecf20Sopenharmony_ci if (inode1 > inode2) 1608c2ecf20Sopenharmony_ci swap(page[0], page[1]); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* Force page buffers uptodate w/o dropping page's lock */ 1668c2ecf20Sopenharmony_cistatic int 1678c2ecf20Sopenharmony_cimext_page_mkuptodate(struct page *page, unsigned from, unsigned to) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct inode *inode = page->mapping->host; 1708c2ecf20Sopenharmony_ci sector_t block; 1718c2ecf20Sopenharmony_ci struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE]; 1728c2ecf20Sopenharmony_ci unsigned int blocksize, block_start, block_end; 1738c2ecf20Sopenharmony_ci int i, err, nr = 0, partial = 0; 1748c2ecf20Sopenharmony_ci BUG_ON(!PageLocked(page)); 1758c2ecf20Sopenharmony_ci BUG_ON(PageWriteback(page)); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (PageUptodate(page)) 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci blocksize = i_blocksize(inode); 1818c2ecf20Sopenharmony_ci if (!page_has_buffers(page)) 1828c2ecf20Sopenharmony_ci create_empty_buffers(page, blocksize, 0); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci head = page_buffers(page); 1858c2ecf20Sopenharmony_ci block = (sector_t)page->index << (PAGE_SHIFT - inode->i_blkbits); 1868c2ecf20Sopenharmony_ci for (bh = head, block_start = 0; bh != head || !block_start; 1878c2ecf20Sopenharmony_ci block++, block_start = block_end, bh = bh->b_this_page) { 1888c2ecf20Sopenharmony_ci block_end = block_start + blocksize; 1898c2ecf20Sopenharmony_ci if (block_end <= from || block_start >= to) { 1908c2ecf20Sopenharmony_ci if (!buffer_uptodate(bh)) 1918c2ecf20Sopenharmony_ci partial = 1; 1928c2ecf20Sopenharmony_ci continue; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci if (buffer_uptodate(bh)) 1958c2ecf20Sopenharmony_ci continue; 1968c2ecf20Sopenharmony_ci if (!buffer_mapped(bh)) { 1978c2ecf20Sopenharmony_ci err = ext4_get_block(inode, block, bh, 0); 1988c2ecf20Sopenharmony_ci if (err) { 1998c2ecf20Sopenharmony_ci SetPageError(page); 2008c2ecf20Sopenharmony_ci return err; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci if (!buffer_mapped(bh)) { 2038c2ecf20Sopenharmony_ci zero_user(page, block_start, blocksize); 2048c2ecf20Sopenharmony_ci set_buffer_uptodate(bh); 2058c2ecf20Sopenharmony_ci continue; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci BUG_ON(nr >= MAX_BUF_PER_PAGE); 2098c2ecf20Sopenharmony_ci arr[nr++] = bh; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci /* No io required */ 2128c2ecf20Sopenharmony_ci if (!nr) 2138c2ecf20Sopenharmony_ci goto out; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci for (i = 0; i < nr; i++) { 2168c2ecf20Sopenharmony_ci bh = arr[i]; 2178c2ecf20Sopenharmony_ci if (!bh_uptodate_or_lock(bh)) { 2188c2ecf20Sopenharmony_ci err = ext4_read_bh(bh, 0, NULL); 2198c2ecf20Sopenharmony_ci if (err) 2208c2ecf20Sopenharmony_ci return err; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ciout: 2248c2ecf20Sopenharmony_ci if (!partial) 2258c2ecf20Sopenharmony_ci SetPageUptodate(page); 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/** 2308c2ecf20Sopenharmony_ci * move_extent_per_page - Move extent data per page 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * @o_filp: file structure of original file 2338c2ecf20Sopenharmony_ci * @donor_inode: donor inode 2348c2ecf20Sopenharmony_ci * @orig_page_offset: page index on original file 2358c2ecf20Sopenharmony_ci * @donor_page_offset: page index on donor file 2368c2ecf20Sopenharmony_ci * @data_offset_in_page: block index where data swapping starts 2378c2ecf20Sopenharmony_ci * @block_len_in_page: the number of blocks to be swapped 2388c2ecf20Sopenharmony_ci * @unwritten: orig extent is unwritten or not 2398c2ecf20Sopenharmony_ci * @err: pointer to save return value 2408c2ecf20Sopenharmony_ci * 2418c2ecf20Sopenharmony_ci * Save the data in original inode blocks and replace original inode extents 2428c2ecf20Sopenharmony_ci * with donor inode extents by calling ext4_swap_extents(). 2438c2ecf20Sopenharmony_ci * Finally, write out the saved data in new original inode blocks. Return 2448c2ecf20Sopenharmony_ci * replaced block count. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_cistatic int 2478c2ecf20Sopenharmony_cimove_extent_per_page(struct file *o_filp, struct inode *donor_inode, 2488c2ecf20Sopenharmony_ci pgoff_t orig_page_offset, pgoff_t donor_page_offset, 2498c2ecf20Sopenharmony_ci int data_offset_in_page, 2508c2ecf20Sopenharmony_ci int block_len_in_page, int unwritten, int *err) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct inode *orig_inode = file_inode(o_filp); 2538c2ecf20Sopenharmony_ci struct page *pagep[2] = {NULL, NULL}; 2548c2ecf20Sopenharmony_ci handle_t *handle; 2558c2ecf20Sopenharmony_ci ext4_lblk_t orig_blk_offset, donor_blk_offset; 2568c2ecf20Sopenharmony_ci unsigned long blocksize = orig_inode->i_sb->s_blocksize; 2578c2ecf20Sopenharmony_ci unsigned int tmp_data_size, data_size, replaced_size; 2588c2ecf20Sopenharmony_ci int i, err2, jblocks, retries = 0; 2598c2ecf20Sopenharmony_ci int replaced_count = 0; 2608c2ecf20Sopenharmony_ci int from = data_offset_in_page << orig_inode->i_blkbits; 2618c2ecf20Sopenharmony_ci int blocks_per_page = PAGE_SIZE >> orig_inode->i_blkbits; 2628c2ecf20Sopenharmony_ci struct super_block *sb = orig_inode->i_sb; 2638c2ecf20Sopenharmony_ci struct buffer_head *bh = NULL; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* 2668c2ecf20Sopenharmony_ci * It needs twice the amount of ordinary journal buffers because 2678c2ecf20Sopenharmony_ci * inode and donor_inode may change each different metadata blocks. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ciagain: 2708c2ecf20Sopenharmony_ci *err = 0; 2718c2ecf20Sopenharmony_ci jblocks = ext4_writepage_trans_blocks(orig_inode) * 2; 2728c2ecf20Sopenharmony_ci handle = ext4_journal_start(orig_inode, EXT4_HT_MOVE_EXTENTS, jblocks); 2738c2ecf20Sopenharmony_ci if (IS_ERR(handle)) { 2748c2ecf20Sopenharmony_ci *err = PTR_ERR(handle); 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci orig_blk_offset = orig_page_offset * blocks_per_page + 2798c2ecf20Sopenharmony_ci data_offset_in_page; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci donor_blk_offset = donor_page_offset * blocks_per_page + 2828c2ecf20Sopenharmony_ci data_offset_in_page; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Calculate data_size */ 2858c2ecf20Sopenharmony_ci if ((orig_blk_offset + block_len_in_page - 1) == 2868c2ecf20Sopenharmony_ci ((orig_inode->i_size - 1) >> orig_inode->i_blkbits)) { 2878c2ecf20Sopenharmony_ci /* Replace the last block */ 2888c2ecf20Sopenharmony_ci tmp_data_size = orig_inode->i_size & (blocksize - 1); 2898c2ecf20Sopenharmony_ci /* 2908c2ecf20Sopenharmony_ci * If data_size equal zero, it shows data_size is multiples of 2918c2ecf20Sopenharmony_ci * blocksize. So we set appropriate value. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ci if (tmp_data_size == 0) 2948c2ecf20Sopenharmony_ci tmp_data_size = blocksize; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci data_size = tmp_data_size + 2978c2ecf20Sopenharmony_ci ((block_len_in_page - 1) << orig_inode->i_blkbits); 2988c2ecf20Sopenharmony_ci } else 2998c2ecf20Sopenharmony_ci data_size = block_len_in_page << orig_inode->i_blkbits; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci replaced_size = data_size; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci *err = mext_page_double_lock(orig_inode, donor_inode, orig_page_offset, 3048c2ecf20Sopenharmony_ci donor_page_offset, pagep); 3058c2ecf20Sopenharmony_ci if (unlikely(*err < 0)) 3068c2ecf20Sopenharmony_ci goto stop_journal; 3078c2ecf20Sopenharmony_ci /* 3088c2ecf20Sopenharmony_ci * If orig extent was unwritten it can become initialized 3098c2ecf20Sopenharmony_ci * at any time after i_data_sem was dropped, in order to 3108c2ecf20Sopenharmony_ci * serialize with delalloc we have recheck extent while we 3118c2ecf20Sopenharmony_ci * hold page's lock, if it is still the case data copy is not 3128c2ecf20Sopenharmony_ci * necessary, just swap data blocks between orig and donor. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci if (unwritten) { 3158c2ecf20Sopenharmony_ci ext4_double_down_write_data_sem(orig_inode, donor_inode); 3168c2ecf20Sopenharmony_ci /* If any of extents in range became initialized we have to 3178c2ecf20Sopenharmony_ci * fallback to data copying */ 3188c2ecf20Sopenharmony_ci unwritten = mext_check_coverage(orig_inode, orig_blk_offset, 3198c2ecf20Sopenharmony_ci block_len_in_page, 1, err); 3208c2ecf20Sopenharmony_ci if (*err) 3218c2ecf20Sopenharmony_ci goto drop_data_sem; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci unwritten &= mext_check_coverage(donor_inode, donor_blk_offset, 3248c2ecf20Sopenharmony_ci block_len_in_page, 1, err); 3258c2ecf20Sopenharmony_ci if (*err) 3268c2ecf20Sopenharmony_ci goto drop_data_sem; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (!unwritten) { 3298c2ecf20Sopenharmony_ci ext4_double_up_write_data_sem(orig_inode, donor_inode); 3308c2ecf20Sopenharmony_ci goto data_copy; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci if ((page_has_private(pagep[0]) && 3338c2ecf20Sopenharmony_ci !try_to_release_page(pagep[0], 0)) || 3348c2ecf20Sopenharmony_ci (page_has_private(pagep[1]) && 3358c2ecf20Sopenharmony_ci !try_to_release_page(pagep[1], 0))) { 3368c2ecf20Sopenharmony_ci *err = -EBUSY; 3378c2ecf20Sopenharmony_ci goto drop_data_sem; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci replaced_count = ext4_swap_extents(handle, orig_inode, 3408c2ecf20Sopenharmony_ci donor_inode, orig_blk_offset, 3418c2ecf20Sopenharmony_ci donor_blk_offset, 3428c2ecf20Sopenharmony_ci block_len_in_page, 1, err); 3438c2ecf20Sopenharmony_ci drop_data_sem: 3448c2ecf20Sopenharmony_ci ext4_double_up_write_data_sem(orig_inode, donor_inode); 3458c2ecf20Sopenharmony_ci goto unlock_pages; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_cidata_copy: 3488c2ecf20Sopenharmony_ci *err = mext_page_mkuptodate(pagep[0], from, from + replaced_size); 3498c2ecf20Sopenharmony_ci if (*err) 3508c2ecf20Sopenharmony_ci goto unlock_pages; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* At this point all buffers in range are uptodate, old mapping layout 3538c2ecf20Sopenharmony_ci * is no longer required, try to drop it now. */ 3548c2ecf20Sopenharmony_ci if ((page_has_private(pagep[0]) && !try_to_release_page(pagep[0], 0)) || 3558c2ecf20Sopenharmony_ci (page_has_private(pagep[1]) && !try_to_release_page(pagep[1], 0))) { 3568c2ecf20Sopenharmony_ci *err = -EBUSY; 3578c2ecf20Sopenharmony_ci goto unlock_pages; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci ext4_double_down_write_data_sem(orig_inode, donor_inode); 3608c2ecf20Sopenharmony_ci replaced_count = ext4_swap_extents(handle, orig_inode, donor_inode, 3618c2ecf20Sopenharmony_ci orig_blk_offset, donor_blk_offset, 3628c2ecf20Sopenharmony_ci block_len_in_page, 1, err); 3638c2ecf20Sopenharmony_ci ext4_double_up_write_data_sem(orig_inode, donor_inode); 3648c2ecf20Sopenharmony_ci if (*err) { 3658c2ecf20Sopenharmony_ci if (replaced_count) { 3668c2ecf20Sopenharmony_ci block_len_in_page = replaced_count; 3678c2ecf20Sopenharmony_ci replaced_size = 3688c2ecf20Sopenharmony_ci block_len_in_page << orig_inode->i_blkbits; 3698c2ecf20Sopenharmony_ci } else 3708c2ecf20Sopenharmony_ci goto unlock_pages; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci /* Perform all necessary steps similar write_begin()/write_end() 3738c2ecf20Sopenharmony_ci * but keeping in mind that i_size will not change */ 3748c2ecf20Sopenharmony_ci if (!page_has_buffers(pagep[0])) 3758c2ecf20Sopenharmony_ci create_empty_buffers(pagep[0], 1 << orig_inode->i_blkbits, 0); 3768c2ecf20Sopenharmony_ci bh = page_buffers(pagep[0]); 3778c2ecf20Sopenharmony_ci for (i = 0; i < data_offset_in_page; i++) 3788c2ecf20Sopenharmony_ci bh = bh->b_this_page; 3798c2ecf20Sopenharmony_ci for (i = 0; i < block_len_in_page; i++) { 3808c2ecf20Sopenharmony_ci *err = ext4_get_block(orig_inode, orig_blk_offset + i, bh, 0); 3818c2ecf20Sopenharmony_ci if (*err < 0) 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci bh = bh->b_this_page; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci if (!*err) 3868c2ecf20Sopenharmony_ci *err = block_commit_write(pagep[0], from, from + replaced_size); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (unlikely(*err < 0)) 3898c2ecf20Sopenharmony_ci goto repair_branches; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* Even in case of data=writeback it is reasonable to pin 3928c2ecf20Sopenharmony_ci * inode to transaction, to prevent unexpected data loss */ 3938c2ecf20Sopenharmony_ci *err = ext4_jbd2_inode_add_write(handle, orig_inode, 3948c2ecf20Sopenharmony_ci (loff_t)orig_page_offset << PAGE_SHIFT, replaced_size); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ciunlock_pages: 3978c2ecf20Sopenharmony_ci unlock_page(pagep[0]); 3988c2ecf20Sopenharmony_ci put_page(pagep[0]); 3998c2ecf20Sopenharmony_ci unlock_page(pagep[1]); 4008c2ecf20Sopenharmony_ci put_page(pagep[1]); 4018c2ecf20Sopenharmony_cistop_journal: 4028c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 4038c2ecf20Sopenharmony_ci if (*err == -ENOSPC && 4048c2ecf20Sopenharmony_ci ext4_should_retry_alloc(sb, &retries)) 4058c2ecf20Sopenharmony_ci goto again; 4068c2ecf20Sopenharmony_ci /* Buffer was busy because probably is pinned to journal transaction, 4078c2ecf20Sopenharmony_ci * force transaction commit may help to free it. */ 4088c2ecf20Sopenharmony_ci if (*err == -EBUSY && retries++ < 4 && EXT4_SB(sb)->s_journal && 4098c2ecf20Sopenharmony_ci jbd2_journal_force_commit_nested(EXT4_SB(sb)->s_journal)) 4108c2ecf20Sopenharmony_ci goto again; 4118c2ecf20Sopenharmony_ci return replaced_count; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cirepair_branches: 4148c2ecf20Sopenharmony_ci /* 4158c2ecf20Sopenharmony_ci * This should never ever happen! 4168c2ecf20Sopenharmony_ci * Extents are swapped already, but we are not able to copy data. 4178c2ecf20Sopenharmony_ci * Try to swap extents to it's original places 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci ext4_double_down_write_data_sem(orig_inode, donor_inode); 4208c2ecf20Sopenharmony_ci replaced_count = ext4_swap_extents(handle, donor_inode, orig_inode, 4218c2ecf20Sopenharmony_ci orig_blk_offset, donor_blk_offset, 4228c2ecf20Sopenharmony_ci block_len_in_page, 0, &err2); 4238c2ecf20Sopenharmony_ci ext4_double_up_write_data_sem(orig_inode, donor_inode); 4248c2ecf20Sopenharmony_ci if (replaced_count != block_len_in_page) { 4258c2ecf20Sopenharmony_ci ext4_error_inode_block(orig_inode, (sector_t)(orig_blk_offset), 4268c2ecf20Sopenharmony_ci EIO, "Unable to copy data block," 4278c2ecf20Sopenharmony_ci " data will be lost."); 4288c2ecf20Sopenharmony_ci *err = -EIO; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci replaced_count = 0; 4318c2ecf20Sopenharmony_ci goto unlock_pages; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci/** 4358c2ecf20Sopenharmony_ci * mext_check_arguments - Check whether move extent can be done 4368c2ecf20Sopenharmony_ci * 4378c2ecf20Sopenharmony_ci * @orig_inode: original inode 4388c2ecf20Sopenharmony_ci * @donor_inode: donor inode 4398c2ecf20Sopenharmony_ci * @orig_start: logical start offset in block for orig 4408c2ecf20Sopenharmony_ci * @donor_start: logical start offset in block for donor 4418c2ecf20Sopenharmony_ci * @len: the number of blocks to be moved 4428c2ecf20Sopenharmony_ci * 4438c2ecf20Sopenharmony_ci * Check the arguments of ext4_move_extents() whether the files can be 4448c2ecf20Sopenharmony_ci * exchanged with each other. 4458c2ecf20Sopenharmony_ci * Return 0 on success, or a negative error value on failure. 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_cistatic int 4488c2ecf20Sopenharmony_cimext_check_arguments(struct inode *orig_inode, 4498c2ecf20Sopenharmony_ci struct inode *donor_inode, __u64 orig_start, 4508c2ecf20Sopenharmony_ci __u64 donor_start, __u64 *len) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci __u64 orig_eof, donor_eof; 4538c2ecf20Sopenharmony_ci unsigned int blkbits = orig_inode->i_blkbits; 4548c2ecf20Sopenharmony_ci unsigned int blocksize = 1 << blkbits; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci orig_eof = (i_size_read(orig_inode) + blocksize - 1) >> blkbits; 4578c2ecf20Sopenharmony_ci donor_eof = (i_size_read(donor_inode) + blocksize - 1) >> blkbits; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (donor_inode->i_mode & (S_ISUID|S_ISGID)) { 4618c2ecf20Sopenharmony_ci ext4_debug("ext4 move extent: suid or sgid is set" 4628c2ecf20Sopenharmony_ci " to donor file [ino:orig %lu, donor %lu]\n", 4638c2ecf20Sopenharmony_ci orig_inode->i_ino, donor_inode->i_ino); 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (IS_IMMUTABLE(donor_inode) || IS_APPEND(donor_inode)) 4688c2ecf20Sopenharmony_ci return -EPERM; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* Ext4 move extent does not support swapfile */ 4718c2ecf20Sopenharmony_ci if (IS_SWAPFILE(orig_inode) || IS_SWAPFILE(donor_inode)) { 4728c2ecf20Sopenharmony_ci ext4_debug("ext4 move extent: The argument files should " 4738c2ecf20Sopenharmony_ci "not be swapfile [ino:orig %lu, donor %lu]\n", 4748c2ecf20Sopenharmony_ci orig_inode->i_ino, donor_inode->i_ino); 4758c2ecf20Sopenharmony_ci return -EBUSY; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (ext4_is_quota_file(orig_inode) && ext4_is_quota_file(donor_inode)) { 4798c2ecf20Sopenharmony_ci ext4_debug("ext4 move extent: The argument files should " 4808c2ecf20Sopenharmony_ci "not be quota files [ino:orig %lu, donor %lu]\n", 4818c2ecf20Sopenharmony_ci orig_inode->i_ino, donor_inode->i_ino); 4828c2ecf20Sopenharmony_ci return -EBUSY; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Ext4 move extent supports only extent based file */ 4868c2ecf20Sopenharmony_ci if (!(ext4_test_inode_flag(orig_inode, EXT4_INODE_EXTENTS))) { 4878c2ecf20Sopenharmony_ci ext4_debug("ext4 move extent: orig file is not extents " 4888c2ecf20Sopenharmony_ci "based file [ino:orig %lu]\n", orig_inode->i_ino); 4898c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4908c2ecf20Sopenharmony_ci } else if (!(ext4_test_inode_flag(donor_inode, EXT4_INODE_EXTENTS))) { 4918c2ecf20Sopenharmony_ci ext4_debug("ext4 move extent: donor file is not extents " 4928c2ecf20Sopenharmony_ci "based file [ino:donor %lu]\n", donor_inode->i_ino); 4938c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if ((!orig_inode->i_size) || (!donor_inode->i_size)) { 4978c2ecf20Sopenharmony_ci ext4_debug("ext4 move extent: File size is 0 byte\n"); 4988c2ecf20Sopenharmony_ci return -EINVAL; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* Start offset should be same */ 5028c2ecf20Sopenharmony_ci if ((orig_start & ~(PAGE_MASK >> orig_inode->i_blkbits)) != 5038c2ecf20Sopenharmony_ci (donor_start & ~(PAGE_MASK >> orig_inode->i_blkbits))) { 5048c2ecf20Sopenharmony_ci ext4_debug("ext4 move extent: orig and donor's start " 5058c2ecf20Sopenharmony_ci "offsets are not aligned [ino:orig %lu, donor %lu]\n", 5068c2ecf20Sopenharmony_ci orig_inode->i_ino, donor_inode->i_ino); 5078c2ecf20Sopenharmony_ci return -EINVAL; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if ((orig_start >= EXT_MAX_BLOCKS) || 5118c2ecf20Sopenharmony_ci (donor_start >= EXT_MAX_BLOCKS) || 5128c2ecf20Sopenharmony_ci (*len > EXT_MAX_BLOCKS) || 5138c2ecf20Sopenharmony_ci (donor_start + *len >= EXT_MAX_BLOCKS) || 5148c2ecf20Sopenharmony_ci (orig_start + *len >= EXT_MAX_BLOCKS)) { 5158c2ecf20Sopenharmony_ci ext4_debug("ext4 move extent: Can't handle over [%u] blocks " 5168c2ecf20Sopenharmony_ci "[ino:orig %lu, donor %lu]\n", EXT_MAX_BLOCKS, 5178c2ecf20Sopenharmony_ci orig_inode->i_ino, donor_inode->i_ino); 5188c2ecf20Sopenharmony_ci return -EINVAL; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci if (orig_eof <= orig_start) 5218c2ecf20Sopenharmony_ci *len = 0; 5228c2ecf20Sopenharmony_ci else if (orig_eof < orig_start + *len - 1) 5238c2ecf20Sopenharmony_ci *len = orig_eof - orig_start; 5248c2ecf20Sopenharmony_ci if (donor_eof <= donor_start) 5258c2ecf20Sopenharmony_ci *len = 0; 5268c2ecf20Sopenharmony_ci else if (donor_eof < donor_start + *len - 1) 5278c2ecf20Sopenharmony_ci *len = donor_eof - donor_start; 5288c2ecf20Sopenharmony_ci if (!*len) { 5298c2ecf20Sopenharmony_ci ext4_debug("ext4 move extent: len should not be 0 " 5308c2ecf20Sopenharmony_ci "[ino:orig %lu, donor %lu]\n", orig_inode->i_ino, 5318c2ecf20Sopenharmony_ci donor_inode->i_ino); 5328c2ecf20Sopenharmony_ci return -EINVAL; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci return 0; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci/** 5398c2ecf20Sopenharmony_ci * ext4_move_extents - Exchange the specified range of a file 5408c2ecf20Sopenharmony_ci * 5418c2ecf20Sopenharmony_ci * @o_filp: file structure of the original file 5428c2ecf20Sopenharmony_ci * @d_filp: file structure of the donor file 5438c2ecf20Sopenharmony_ci * @orig_blk: start offset in block for orig 5448c2ecf20Sopenharmony_ci * @donor_blk: start offset in block for donor 5458c2ecf20Sopenharmony_ci * @len: the number of blocks to be moved 5468c2ecf20Sopenharmony_ci * @moved_len: moved block length 5478c2ecf20Sopenharmony_ci * 5488c2ecf20Sopenharmony_ci * This function returns 0 and moved block length is set in moved_len 5498c2ecf20Sopenharmony_ci * if succeed, otherwise returns error value. 5508c2ecf20Sopenharmony_ci * 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_ciint 5538c2ecf20Sopenharmony_ciext4_move_extents(struct file *o_filp, struct file *d_filp, __u64 orig_blk, 5548c2ecf20Sopenharmony_ci __u64 donor_blk, __u64 len, __u64 *moved_len) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct inode *orig_inode = file_inode(o_filp); 5578c2ecf20Sopenharmony_ci struct inode *donor_inode = file_inode(d_filp); 5588c2ecf20Sopenharmony_ci struct ext4_ext_path *path = NULL; 5598c2ecf20Sopenharmony_ci int blocks_per_page = PAGE_SIZE >> orig_inode->i_blkbits; 5608c2ecf20Sopenharmony_ci ext4_lblk_t o_end, o_start = orig_blk; 5618c2ecf20Sopenharmony_ci ext4_lblk_t d_start = donor_blk; 5628c2ecf20Sopenharmony_ci int ret; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (orig_inode->i_sb != donor_inode->i_sb) { 5658c2ecf20Sopenharmony_ci ext4_debug("ext4 move extent: The argument files " 5668c2ecf20Sopenharmony_ci "should be in same FS [ino:orig %lu, donor %lu]\n", 5678c2ecf20Sopenharmony_ci orig_inode->i_ino, donor_inode->i_ino); 5688c2ecf20Sopenharmony_ci return -EINVAL; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* orig and donor should be different inodes */ 5728c2ecf20Sopenharmony_ci if (orig_inode == donor_inode) { 5738c2ecf20Sopenharmony_ci ext4_debug("ext4 move extent: The argument files should not " 5748c2ecf20Sopenharmony_ci "be same inode [ino:orig %lu, donor %lu]\n", 5758c2ecf20Sopenharmony_ci orig_inode->i_ino, donor_inode->i_ino); 5768c2ecf20Sopenharmony_ci return -EINVAL; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* Regular file check */ 5808c2ecf20Sopenharmony_ci if (!S_ISREG(orig_inode->i_mode) || !S_ISREG(donor_inode->i_mode)) { 5818c2ecf20Sopenharmony_ci ext4_debug("ext4 move extent: The argument files should be " 5828c2ecf20Sopenharmony_ci "regular file [ino:orig %lu, donor %lu]\n", 5838c2ecf20Sopenharmony_ci orig_inode->i_ino, donor_inode->i_ino); 5848c2ecf20Sopenharmony_ci return -EINVAL; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* TODO: it's not obvious how to swap blocks for inodes with full 5888c2ecf20Sopenharmony_ci journaling enabled */ 5898c2ecf20Sopenharmony_ci if (ext4_should_journal_data(orig_inode) || 5908c2ecf20Sopenharmony_ci ext4_should_journal_data(donor_inode)) { 5918c2ecf20Sopenharmony_ci ext4_msg(orig_inode->i_sb, KERN_ERR, 5928c2ecf20Sopenharmony_ci "Online defrag not supported with data journaling"); 5938c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (IS_ENCRYPTED(orig_inode) || IS_ENCRYPTED(donor_inode)) { 5978c2ecf20Sopenharmony_ci ext4_msg(orig_inode->i_sb, KERN_ERR, 5988c2ecf20Sopenharmony_ci "Online defrag not supported for encrypted files"); 5998c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* Protect orig and donor inodes against a truncate */ 6038c2ecf20Sopenharmony_ci lock_two_nondirectories(orig_inode, donor_inode); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* Wait for all existing dio workers */ 6068c2ecf20Sopenharmony_ci inode_dio_wait(orig_inode); 6078c2ecf20Sopenharmony_ci inode_dio_wait(donor_inode); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* Protect extent tree against block allocations via delalloc */ 6108c2ecf20Sopenharmony_ci ext4_double_down_write_data_sem(orig_inode, donor_inode); 6118c2ecf20Sopenharmony_ci /* Check the filesystem environment whether move_extent can be done */ 6128c2ecf20Sopenharmony_ci ret = mext_check_arguments(orig_inode, donor_inode, orig_blk, 6138c2ecf20Sopenharmony_ci donor_blk, &len); 6148c2ecf20Sopenharmony_ci if (ret) 6158c2ecf20Sopenharmony_ci goto out; 6168c2ecf20Sopenharmony_ci o_end = o_start + len; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci *moved_len = 0; 6198c2ecf20Sopenharmony_ci while (o_start < o_end) { 6208c2ecf20Sopenharmony_ci struct ext4_extent *ex; 6218c2ecf20Sopenharmony_ci ext4_lblk_t cur_blk, next_blk; 6228c2ecf20Sopenharmony_ci pgoff_t orig_page_index, donor_page_index; 6238c2ecf20Sopenharmony_ci int offset_in_page; 6248c2ecf20Sopenharmony_ci int unwritten, cur_len; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci ret = get_ext_path(orig_inode, o_start, &path); 6278c2ecf20Sopenharmony_ci if (ret) 6288c2ecf20Sopenharmony_ci goto out; 6298c2ecf20Sopenharmony_ci ex = path[path->p_depth].p_ext; 6308c2ecf20Sopenharmony_ci next_blk = ext4_ext_next_allocated_block(path); 6318c2ecf20Sopenharmony_ci cur_blk = le32_to_cpu(ex->ee_block); 6328c2ecf20Sopenharmony_ci cur_len = ext4_ext_get_actual_len(ex); 6338c2ecf20Sopenharmony_ci /* Check hole before the start pos */ 6348c2ecf20Sopenharmony_ci if (cur_blk + cur_len - 1 < o_start) { 6358c2ecf20Sopenharmony_ci if (next_blk == EXT_MAX_BLOCKS) { 6368c2ecf20Sopenharmony_ci o_start = o_end; 6378c2ecf20Sopenharmony_ci ret = -ENODATA; 6388c2ecf20Sopenharmony_ci goto out; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci d_start += next_blk - o_start; 6418c2ecf20Sopenharmony_ci o_start = next_blk; 6428c2ecf20Sopenharmony_ci continue; 6438c2ecf20Sopenharmony_ci /* Check hole after the start pos */ 6448c2ecf20Sopenharmony_ci } else if (cur_blk > o_start) { 6458c2ecf20Sopenharmony_ci /* Skip hole */ 6468c2ecf20Sopenharmony_ci d_start += cur_blk - o_start; 6478c2ecf20Sopenharmony_ci o_start = cur_blk; 6488c2ecf20Sopenharmony_ci /* Extent inside requested range ?*/ 6498c2ecf20Sopenharmony_ci if (cur_blk >= o_end) 6508c2ecf20Sopenharmony_ci goto out; 6518c2ecf20Sopenharmony_ci } else { /* in_range(o_start, o_blk, o_len) */ 6528c2ecf20Sopenharmony_ci cur_len += cur_blk - o_start; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci unwritten = ext4_ext_is_unwritten(ex); 6558c2ecf20Sopenharmony_ci if (o_end - o_start < cur_len) 6568c2ecf20Sopenharmony_ci cur_len = o_end - o_start; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci orig_page_index = o_start >> (PAGE_SHIFT - 6598c2ecf20Sopenharmony_ci orig_inode->i_blkbits); 6608c2ecf20Sopenharmony_ci donor_page_index = d_start >> (PAGE_SHIFT - 6618c2ecf20Sopenharmony_ci donor_inode->i_blkbits); 6628c2ecf20Sopenharmony_ci offset_in_page = o_start % blocks_per_page; 6638c2ecf20Sopenharmony_ci if (cur_len > blocks_per_page- offset_in_page) 6648c2ecf20Sopenharmony_ci cur_len = blocks_per_page - offset_in_page; 6658c2ecf20Sopenharmony_ci /* 6668c2ecf20Sopenharmony_ci * Up semaphore to avoid following problems: 6678c2ecf20Sopenharmony_ci * a. transaction deadlock among ext4_journal_start, 6688c2ecf20Sopenharmony_ci * ->write_begin via pagefault, and jbd2_journal_commit 6698c2ecf20Sopenharmony_ci * b. racing with ->readpage, ->write_begin, and ext4_get_block 6708c2ecf20Sopenharmony_ci * in move_extent_per_page 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_ci ext4_double_up_write_data_sem(orig_inode, donor_inode); 6738c2ecf20Sopenharmony_ci /* Swap original branches with new branches */ 6748c2ecf20Sopenharmony_ci *moved_len += move_extent_per_page(o_filp, donor_inode, 6758c2ecf20Sopenharmony_ci orig_page_index, donor_page_index, 6768c2ecf20Sopenharmony_ci offset_in_page, cur_len, 6778c2ecf20Sopenharmony_ci unwritten, &ret); 6788c2ecf20Sopenharmony_ci ext4_double_down_write_data_sem(orig_inode, donor_inode); 6798c2ecf20Sopenharmony_ci if (ret < 0) 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci o_start += cur_len; 6828c2ecf20Sopenharmony_ci d_start += cur_len; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ciout: 6868c2ecf20Sopenharmony_ci if (*moved_len) { 6878c2ecf20Sopenharmony_ci ext4_discard_preallocations(orig_inode, 0); 6888c2ecf20Sopenharmony_ci ext4_discard_preallocations(donor_inode, 0); 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci ext4_ext_drop_refs(path); 6928c2ecf20Sopenharmony_ci kfree(path); 6938c2ecf20Sopenharmony_ci ext4_double_up_write_data_sem(orig_inode, donor_inode); 6948c2ecf20Sopenharmony_ci unlock_two_nondirectories(orig_inode, donor_inode); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci return ret; 6978c2ecf20Sopenharmony_ci} 698