| 1 | |
| 2 | |
| 3 | * Copyright (c) 2008,2009 NEC Software Tohoku, Ltd.
|
| 4 | * Written by Takashi Sato <[email protected]>
|
| 5 | * Akira Fujita <[email protected]>
|
| 6 |
|
| 7 |
|
| 8 | #include <linux/fs.h>
|
| 9 | #include <linux/quotaops.h>
|
| 10 | #include <linux/slab.h>
|
| 11 | #include <linux/sched/mm.h>
|
| 12 | #include "ext4_jbd2.h"
|
| 13 | #include "ext4.h"
|
| 14 | #include "ext4_extents.h"
|
| 15 |
|
| 16 | #include <trace/events/ext4.h>
|
| 17 |
|
| 18 | struct mext_data {
|
| 19 | struct inode *orig_inode;
|
| 20 | struct inode *donor_inode;
|
| 21 | struct ext4_map_blocks orig_map;
|
| 22 | ext4_lblk_t donor_lblk;
|
| 23 | };
|
| 24 |
|
| 25 | |
| 26 | * ext4_double_down_write_data_sem() - write lock two inodes's i_data_sem
|
| 27 | * @first: inode to be locked
|
| 28 | * @second: inode to be locked
|
| 29 | *
|
| 30 | * Acquire write lock of i_data_sem of the two inodes
|
| 31 |
|
| 32 | void
|
| 33 | ext4_double_down_write_data_sem(struct inode *first, struct inode *second)
|
| 34 | {
|
| 35 | if (first < second) {
|
| 36 | down_write(&EXT4_I(first)->i_data_sem);
|
| 37 | down_write_nested(&EXT4_I(second)->i_data_sem, I_DATA_SEM_OTHER);
|
| 38 | } else {
|
| 39 | down_write(&EXT4_I(second)->i_data_sem);
|
| 40 | down_write_nested(&EXT4_I(first)->i_data_sem, I_DATA_SEM_OTHER);
|
| 41 | }
|
| 42 | }
|
| 43 |
|
| 44 | |
| 45 | * ext4_double_up_write_data_sem - Release two inodes' write lock of i_data_sem
|
| 46 | *
|
| 47 | * @orig_inode: original inode structure to be released its lock first
|
| 48 | * @donor_inode: donor inode structure to be released its lock second
|
| 49 | * Release write lock of i_data_sem of two inodes (orig and donor).
|
| 50 |
|
| 51 | void
|
| 52 | ext4_double_up_write_data_sem(struct inode *orig_inode,
|
| 53 | struct inode *donor_inode)
|
| 54 | {
|
| 55 | up_write(&EXT4_I(orig_inode)->i_data_sem);
|
| 56 | up_write(&EXT4_I(donor_inode)->i_data_sem);
|
| 57 | }
|
| 58 |
|
| 59 |
|
| 60 | static int mext_folio_double_lock(struct inode *inode1, struct inode *inode2,
|
| 61 | pgoff_t index1, pgoff_t index2, size_t len,
|
| 62 | struct folio *folio[2])
|
| 63 | {
|
| 64 | struct address_space *mapping[2];
|
| 65 | unsigned int flags;
|
| 66 | fgf_t fgp_flags = FGP_WRITEBEGIN;
|
| 67 |
|
| 68 | BUG_ON(!inode1 || !inode2);
|
| 69 | if (inode1 < inode2) {
|
| 70 | mapping[0] = inode1->i_mapping;
|
| 71 | mapping[1] = inode2->i_mapping;
|
| 72 | } else {
|
| 73 | swap(index1, index2);
|
| 74 | mapping[0] = inode2->i_mapping;
|
| 75 | mapping[1] = inode1->i_mapping;
|
| 76 | }
|
| 77 |
|
| 78 | flags = memalloc_nofs_save();
|
| 79 | fgp_flags |= fgf_set_order(len);
|
| 80 | folio[0] = __filemap_get_folio(mapping[0], index1, fgp_flags,
|
| 81 | mapping_gfp_mask(mapping[0]));
|
| 82 | if (IS_ERR(folio[0])) {
|
| 83 | memalloc_nofs_restore(flags);
|
| 84 | return PTR_ERR(folio[0]);
|
| 85 | }
|
| 86 |
|
| 87 | folio[1] = __filemap_get_folio(mapping[1], index2, fgp_flags,
|
| 88 | mapping_gfp_mask(mapping[1]));
|
| 89 | memalloc_nofs_restore(flags);
|
| 90 | if (IS_ERR(folio[1])) {
|
| 91 | folio_unlock(folio[0]);
|
| 92 | folio_put(folio[0]);
|
| 93 | return PTR_ERR(folio[1]);
|
| 94 | }
|
| 95 | |
| 96 | * __filemap_get_folio() may not wait on folio's writeback if
|
| 97 | * BDI not demand that. But it is reasonable to be very conservative
|
| 98 | * here and explicitly wait on folio's writeback
|
| 99 |
|
| 100 | folio_wait_writeback(folio[0]);
|
| 101 | folio_wait_writeback(folio[1]);
|
| 102 | if (inode1 > inode2)
|
| 103 | swap(folio[0], folio[1]);
|
| 104 |
|
| 105 | return 0;
|
| 106 | }
|
| 107 |
|
| 108 | static void mext_folio_double_unlock(struct folio *folio[2])
|
| 109 | {
|
| 110 | folio_unlock(folio[0]);
|
| 111 | folio_put(folio[0]);
|
| 112 | folio_unlock(folio[1]);
|
| 113 | folio_put(folio[1]);
|
| 114 | }
|
| 115 |
|
| 116 |
|
| 117 | static int mext_folio_mkuptodate(struct folio *folio, size_t from, size_t to)
|
| 118 | {
|
| 119 | struct inode *inode = folio->mapping->host;
|
| 120 | sector_t block;
|
| 121 | struct buffer_head *bh, *head;
|
| 122 | unsigned int blocksize, block_start, block_end;
|
| 123 | int nr = 0;
|
| 124 | bool partial = false;
|
| 125 |
|
| 126 | BUG_ON(!folio_test_locked(folio));
|
| 127 | BUG_ON(folio_test_writeback(folio));
|
| 128 |
|
| 129 | if (folio_test_uptodate(folio))
|
| 130 | return 0;
|
| 131 |
|
| 132 | blocksize = i_blocksize(inode);
|
| 133 | head = folio_buffers(folio);
|
| 134 | if (!head)
|
| 135 | head = create_empty_buffers(folio, blocksize, 0);
|
| 136 |
|
| 137 | block = folio_pos(folio) >> inode->i_blkbits;
|
| 138 | block_end = 0;
|
| 139 | bh = head;
|
| 140 | do {
|
| 141 | block_start = block_end;
|
| 142 | block_end = block_start + blocksize;
|
| 143 | if (block_end <= from || block_start >= to) {
|
| 144 | if (!buffer_uptodate(bh))
|
| 145 | partial = true;
|
| 146 | continue;
|
| 147 | }
|
| 148 | if (buffer_uptodate(bh))
|
| 149 | continue;
|
| 150 | if (!buffer_mapped(bh)) {
|
| 151 | int err = ext4_get_block(inode, block, bh, 0);
|
| 152 | if (err)
|
| 153 | return err;
|
| 154 | if (!buffer_mapped(bh)) {
|
| 155 | folio_zero_range(folio, block_start, blocksize);
|
| 156 | set_buffer_uptodate(bh);
|
| 157 | continue;
|
| 158 | }
|
| 159 | }
|
| 160 | lock_buffer(bh);
|
| 161 | if (buffer_uptodate(bh)) {
|
| 162 | unlock_buffer(bh);
|
| 163 | continue;
|
| 164 | }
|
| 165 | ext4_read_bh_nowait(bh, 0, NULL, false);
|
| 166 | nr++;
|
| 167 | } while (block++, (bh = bh->b_this_page) != head);
|
| 168 |
|
| 169 |
|
| 170 | if (!nr)
|
| 171 | goto out;
|
| 172 |
|
| 173 | bh = head;
|
| 174 | do {
|
| 175 | if (bh_offset(bh) + blocksize <= from)
|
| 176 | continue;
|
| 177 | if (bh_offset(bh) >= to)
|
| 178 | break;
|
| 179 | wait_on_buffer(bh);
|
| 180 | if (buffer_uptodate(bh))
|
| 181 | continue;
|
| 182 | return -EIO;
|
| 183 | } while ((bh = bh->b_this_page) != head);
|
| 184 | out:
|
| 185 | if (!partial)
|
| 186 | folio_mark_uptodate(folio);
|
| 187 | return 0;
|
| 188 | }
|
| 189 |
|
| 190 | enum mext_move_type {MEXT_SKIP_EXTENT, MEXT_MOVE_EXTENT, MEXT_COPY_DATA};
|
| 191 |
|
| 192 | |
| 193 | * Start to move extent between the origin inode and the donor inode,
|
| 194 | * hold one folio for each inode and check the candidate moving extent
|
| 195 | * mapping status again.
|
| 196 |
|
| 197 | static int mext_move_begin(struct mext_data *mext, struct folio *folio[2],
|
| 198 | enum mext_move_type *move_type)
|
| 199 | {
|
| 200 | struct inode *orig_inode = mext->orig_inode;
|
| 201 | struct inode *donor_inode = mext->donor_inode;
|
| 202 | unsigned int blkbits = orig_inode->i_blkbits;
|
| 203 | struct ext4_map_blocks donor_map = {0};
|
| 204 | loff_t orig_pos, donor_pos;
|
| 205 | size_t move_len;
|
| 206 | int ret;
|
| 207 |
|
| 208 | orig_pos = ((loff_t)mext->orig_map.m_lblk) << blkbits;
|
| 209 | donor_pos = ((loff_t)mext->donor_lblk) << blkbits;
|
| 210 | ret = mext_folio_double_lock(orig_inode, donor_inode,
|
| 211 | orig_pos >> PAGE_SHIFT, donor_pos >> PAGE_SHIFT,
|
| 212 | ((size_t)mext->orig_map.m_len) << blkbits, folio);
|
| 213 | if (ret)
|
| 214 | return ret;
|
| 215 |
|
| 216 | |
| 217 | * Check the origin inode's mapping information again under the
|
| 218 | * folio lock, as we do not hold the i_data_sem at all times, and
|
| 219 | * it may change during the concurrent write-back operation.
|
| 220 |
|
| 221 | if (mext->orig_map.m_seq != READ_ONCE(EXT4_I(orig_inode)->i_es_seq)) {
|
| 222 | ret = -ESTALE;
|
| 223 | goto error;
|
| 224 | }
|
| 225 |
|
| 226 |
|
| 227 | move_len = umin(folio_next_pos(folio[0]) - orig_pos,
|
| 228 | folio_next_pos(folio[1]) - donor_pos);
|
| 229 | move_len >>= blkbits;
|
| 230 | if (move_len < mext->orig_map.m_len)
|
| 231 | mext->orig_map.m_len = move_len;
|
| 232 |
|
| 233 | donor_map.m_lblk = mext->donor_lblk;
|
| 234 | donor_map.m_len = mext->orig_map.m_len;
|
| 235 | donor_map.m_flags = 0;
|
| 236 | ret = ext4_map_blocks(NULL, donor_inode, &donor_map, 0);
|
| 237 | if (ret < 0)
|
| 238 | goto error;
|
| 239 |
|
| 240 |
|
| 241 | mext->orig_map.m_len = donor_map.m_len;
|
| 242 |
|
| 243 |
|
| 244 | if (!(donor_map.m_flags & (EXT4_MAP_MAPPED | EXT4_MAP_UNWRITTEN)))
|
| 245 | *move_type = MEXT_SKIP_EXTENT;
|
| 246 |
|
| 247 | else if ((mext->orig_map.m_flags & EXT4_MAP_UNWRITTEN) &&
|
| 248 | (donor_map.m_flags & EXT4_MAP_UNWRITTEN))
|
| 249 | *move_type = MEXT_MOVE_EXTENT;
|
| 250 | else
|
| 251 | *move_type = MEXT_COPY_DATA;
|
| 252 |
|
| 253 | return 0;
|
| 254 | error:
|
| 255 | mext_folio_double_unlock(folio);
|
| 256 | return ret;
|
| 257 | }
|
| 258 |
|
| 259 | |
| 260 | * Re-create the new moved mapping buffers of the original inode and commit
|
| 261 | * the entire written range.
|
| 262 |
|
| 263 | static int mext_folio_mkwrite(struct inode *inode, struct folio *folio,
|
| 264 | size_t from, size_t to)
|
| 265 | {
|
| 266 | unsigned int blocksize = i_blocksize(inode);
|
| 267 | struct buffer_head *bh, *head;
|
| 268 | size_t block_start, block_end;
|
| 269 | sector_t block;
|
| 270 | int ret;
|
| 271 |
|
| 272 | head = folio_buffers(folio);
|
| 273 | if (!head)
|
| 274 | head = create_empty_buffers(folio, blocksize, 0);
|
| 275 |
|
| 276 | block = folio_pos(folio) >> inode->i_blkbits;
|
| 277 | block_end = 0;
|
| 278 | bh = head;
|
| 279 | do {
|
| 280 | block_start = block_end;
|
| 281 | block_end = block_start + blocksize;
|
| 282 | if (block_end <= from || block_start >= to)
|
| 283 | continue;
|
| 284 |
|
| 285 | ret = ext4_get_block(inode, block, bh, 0);
|
| 286 | if (ret)
|
| 287 | return ret;
|
| 288 | } while (block++, (bh = bh->b_this_page) != head);
|
| 289 |
|
| 290 | block_commit_write(folio, from, to);
|
| 291 | return 0;
|
| 292 | }
|
| 293 |
|
| 294 | |
| 295 | * Save the data in original inode extent blocks and replace one folio size
|
| 296 | * aligned original inode extent with one or one partial donor inode extent,
|
| 297 | * and then write out the saved data in new original inode blocks. Pass out
|
| 298 | * the replaced block count through m_len. Return 0 on success, and an error
|
| 299 | * code otherwise.
|
| 300 |
|
| 301 | static int mext_move_extent(struct mext_data *mext, u64 *m_len)
|
| 302 | {
|
| 303 | struct inode *orig_inode = mext->orig_inode;
|
| 304 | struct inode *donor_inode = mext->donor_inode;
|
| 305 | struct ext4_map_blocks *orig_map = &mext->orig_map;
|
| 306 | unsigned int blkbits = orig_inode->i_blkbits;
|
| 307 | struct folio *folio[2] = {NULL, NULL};
|
| 308 | loff_t from, length;
|
| 309 | enum mext_move_type move_type = 0;
|
| 310 | handle_t *handle;
|
| 311 | u64 r_len = 0;
|
| 312 | unsigned int credits;
|
| 313 | int ret, ret2;
|
| 314 |
|
| 315 | *m_len = 0;
|
| 316 | trace_ext4_move_extent_enter(orig_inode, orig_map, donor_inode,
|
| 317 | mext->donor_lblk);
|
| 318 | credits = ext4_chunk_trans_extent(orig_inode, 0) * 2;
|
| 319 | handle = ext4_journal_start(orig_inode, EXT4_HT_MOVE_EXTENTS, credits);
|
| 320 | if (IS_ERR(handle)) {
|
| 321 | ret = PTR_ERR(handle);
|
| 322 | goto out;
|
| 323 | }
|
| 324 | ext4_fc_mark_ineligible(orig_inode->i_sb, EXT4_FC_REASON_MOVE_EXT,
|
| 325 | handle);
|
| 326 |
|
| 327 | ret = mext_move_begin(mext, folio, &move_type);
|
| 328 | if (ret)
|
| 329 | goto stop_handle;
|
| 330 |
|
| 331 | if (move_type == MEXT_SKIP_EXTENT)
|
| 332 | goto unlock;
|
| 333 |
|
| 334 | |
| 335 | * Copy the data. First, read the original inode data into the page
|
| 336 | * cache. Then, release the existing mapping relationships and swap
|
| 337 | * the extent. Finally, re-establish the new mapping relationships
|
| 338 | * and dirty the page cache.
|
| 339 |
|
| 340 | if (move_type == MEXT_COPY_DATA) {
|
| 341 | from = offset_in_folio(folio[0],
|
| 342 | ((loff_t)orig_map->m_lblk) << blkbits);
|
| 343 | length = ((loff_t)orig_map->m_len) << blkbits;
|
| 344 |
|
| 345 | ret = mext_folio_mkuptodate(folio[0], from, from + length);
|
| 346 | if (ret)
|
| 347 | goto unlock;
|
| 348 | }
|
| 349 |
|
| 350 | if (!filemap_release_folio(folio[0], 0) ||
|
| 351 | !filemap_release_folio(folio[1], 0)) {
|
| 352 | ret = -EBUSY;
|
| 353 | goto unlock;
|
| 354 | }
|
| 355 |
|
| 356 |
|
| 357 | ext4_double_down_write_data_sem(orig_inode, donor_inode);
|
| 358 | *m_len = ext4_swap_extents(handle, orig_inode, donor_inode,
|
| 359 | orig_map->m_lblk, mext->donor_lblk,
|
| 360 | orig_map->m_len, 1, &ret);
|
| 361 | ext4_double_up_write_data_sem(orig_inode, donor_inode);
|
| 362 |
|
| 363 |
|
| 364 | if (WARN_ON_ONCE(!ret && (*m_len != orig_map->m_len)))
|
| 365 | ret = -EIO;
|
| 366 |
|
| 367 | if (!(*m_len) || (move_type == MEXT_MOVE_EXTENT))
|
| 368 | goto unlock;
|
| 369 |
|
| 370 |
|
| 371 | length = (*m_len) << blkbits;
|
| 372 | ret2 = mext_folio_mkwrite(orig_inode, folio[0], from, from + length);
|
| 373 | if (ret2) {
|
| 374 | if (!ret)
|
| 375 | ret = ret2;
|
| 376 | goto repair_branches;
|
| 377 | }
|
| 378 | |
| 379 | * Even in case of data=writeback it is reasonable to pin
|
| 380 | * inode to transaction, to prevent unexpected data loss.
|
| 381 |
|
| 382 | ret2 = ext4_jbd2_inode_add_write(handle, orig_inode,
|
| 383 | ((loff_t)orig_map->m_lblk) << blkbits, length);
|
| 384 | if (!ret)
|
| 385 | ret = ret2;
|
| 386 | unlock:
|
| 387 | mext_folio_double_unlock(folio);
|
| 388 | stop_handle:
|
| 389 | ext4_journal_stop(handle);
|
| 390 | out:
|
| 391 | trace_ext4_move_extent_exit(orig_inode, orig_map->m_lblk, donor_inode,
|
| 392 | mext->donor_lblk, orig_map->m_len, *m_len,
|
| 393 | move_type, ret);
|
| 394 | return ret;
|
| 395 |
|
| 396 | repair_branches:
|
| 397 | ret2 = 0;
|
| 398 | ext4_double_down_write_data_sem(orig_inode, donor_inode);
|
| 399 | r_len = ext4_swap_extents(handle, donor_inode, orig_inode,
|
| 400 | mext->donor_lblk, orig_map->m_lblk,
|
| 401 | *m_len, 0, &ret2);
|
| 402 | ext4_double_up_write_data_sem(orig_inode, donor_inode);
|
| 403 | if (ret2 || r_len != *m_len) {
|
| 404 | ext4_error_inode_block(orig_inode, (sector_t)(orig_map->m_lblk),
|
| 405 | EIO, "Unable to copy data block, data will be lost!");
|
| 406 | ret = -EIO;
|
| 407 | }
|
| 408 | *m_len = 0;
|
| 409 | goto unlock;
|
| 410 | }
|
| 411 |
|
| 412 | |
| 413 | * Check the validity of the basic filesystem environment and the
|
| 414 | * inodes' support status.
|
| 415 |
|
| 416 | static int mext_check_validity(struct inode *orig_inode,
|
| 417 | struct inode *donor_inode)
|
| 418 | {
|
| 419 | struct super_block *sb = orig_inode->i_sb;
|
| 420 |
|
| 421 |
|
| 422 | if (orig_inode == donor_inode) {
|
| 423 | ext4_debug("ext4 move extent: The argument files should not be same inode [ino:orig %llu, donor %llu]\n",
|
| 424 | orig_inode->i_ino, donor_inode->i_ino);
|
| 425 | return -EINVAL;
|
| 426 | }
|
| 427 |
|
| 428 |
|
| 429 | if (orig_inode->i_sb != donor_inode->i_sb) {
|
| 430 | ext4_debug("ext4 move extent: The argument files should be in same FS [ino:orig %llu, donor %llu]\n",
|
| 431 | orig_inode->i_ino, donor_inode->i_ino);
|
| 432 | return -EINVAL;
|
| 433 | }
|
| 434 |
|
| 435 |
|
| 436 | if (!S_ISREG(orig_inode->i_mode) || !S_ISREG(donor_inode->i_mode)) {
|
| 437 | ext4_debug("ext4 move extent: The argument files should be regular file [ino:orig %llu, donor %llu]\n",
|
| 438 | orig_inode->i_ino, donor_inode->i_ino);
|
| 439 | return -EINVAL;
|
| 440 | }
|
| 441 |
|
| 442 | if (ext4_has_feature_bigalloc(sb)) {
|
| 443 | ext4_msg(sb, KERN_ERR,
|
| 444 | "Online defrag not supported with bigalloc");
|
| 445 | return -EOPNOTSUPP;
|
| 446 | }
|
| 447 |
|
| 448 | if (IS_DAX(orig_inode)) {
|
| 449 | ext4_msg(sb, KERN_ERR,
|
| 450 | "Online defrag not supported with DAX");
|
| 451 | return -EOPNOTSUPP;
|
| 452 | }
|
| 453 |
|
| 454 | |
| 455 | * TODO: it's not obvious how to swap blocks for inodes with full
|
| 456 | * journaling enabled.
|
| 457 |
|
| 458 | if (ext4_should_journal_data(orig_inode) ||
|
| 459 | ext4_should_journal_data(donor_inode)) {
|
| 460 | ext4_msg(sb, KERN_ERR,
|
| 461 | "Online defrag not supported with data journaling");
|
| 462 | return -EOPNOTSUPP;
|
| 463 | }
|
| 464 |
|
| 465 | if (IS_ENCRYPTED(orig_inode) || IS_ENCRYPTED(donor_inode)) {
|
| 466 | ext4_msg(sb, KERN_ERR,
|
| 467 | "Online defrag not supported for encrypted files");
|
| 468 | return -EOPNOTSUPP;
|
| 469 | }
|
| 470 |
|
| 471 |
|
| 472 | if (!(ext4_test_inode_flag(orig_inode, EXT4_INODE_EXTENTS)) ||
|
| 473 | !(ext4_test_inode_flag(donor_inode, EXT4_INODE_EXTENTS))) {
|
| 474 | ext4_msg(sb, KERN_ERR,
|
| 475 | "Online defrag not supported for non-extent files");
|
| 476 | return -EOPNOTSUPP;
|
| 477 | }
|
| 478 |
|
| 479 | if (donor_inode->i_mode & (S_ISUID|S_ISGID)) {
|
| 480 | ext4_debug("ext4 move extent: suid or sgid is set to donor file [ino:orig %llu, donor %llu]\n",
|
| 481 | orig_inode->i_ino, donor_inode->i_ino);
|
| 482 | return -EINVAL;
|
| 483 | }
|
| 484 |
|
| 485 | if (IS_IMMUTABLE(donor_inode) || IS_APPEND(donor_inode)) {
|
| 486 | ext4_debug("ext4 move extent: donor should not be immutable or append file [ino:orig %llu, donor %llu]\n",
|
| 487 | orig_inode->i_ino, donor_inode->i_ino);
|
| 488 | return -EPERM;
|
| 489 | }
|
| 490 |
|
| 491 |
|
| 492 | if (IS_SWAPFILE(orig_inode) || IS_SWAPFILE(donor_inode)) {
|
| 493 | ext4_debug("ext4 move extent: The argument files should not be swap files [ino:orig %llu, donor %llu]\n",
|
| 494 | orig_inode->i_ino, donor_inode->i_ino);
|
| 495 | return -ETXTBSY;
|
| 496 | }
|
| 497 |
|
| 498 | if (ext4_is_quota_file(orig_inode) || ext4_is_quota_file(donor_inode)) {
|
| 499 | ext4_debug("ext4 move extent: The argument files should not be quota files [ino:orig %llu, donor %llu]\n",
|
| 500 | orig_inode->i_ino, donor_inode->i_ino);
|
| 501 | return -EOPNOTSUPP;
|
| 502 | }
|
| 503 |
|
| 504 | if ((!orig_inode->i_size) || (!donor_inode->i_size)) {
|
| 505 | ext4_debug("ext4 move extent: File size is 0 byte\n");
|
| 506 | return -EINVAL;
|
| 507 | }
|
| 508 |
|
| 509 | return 0;
|
| 510 | }
|
| 511 |
|
| 512 | |
| 513 | * Check the moving range of ext4_move_extents() whether the files can be
|
| 514 | * exchanged with each other, and adjust the length to fit within the file
|
| 515 | * size. Return 0 on success, or a negative error value on failure.
|
| 516 |
|
| 517 | static int mext_check_adjust_range(struct inode *orig_inode,
|
| 518 | struct inode *donor_inode, __u64 orig_start,
|
| 519 | __u64 donor_start, __u64 *len)
|
| 520 | {
|
| 521 | __u64 orig_eof, donor_eof;
|
| 522 |
|
| 523 |
|
| 524 | if ((orig_start & ~(PAGE_MASK >> orig_inode->i_blkbits)) !=
|
| 525 | (donor_start & ~(PAGE_MASK >> orig_inode->i_blkbits))) {
|
| 526 | ext4_debug("ext4 move extent: orig and donor's start offsets are not aligned [ino:orig %llu, donor %llu]\n",
|
| 527 | orig_inode->i_ino, donor_inode->i_ino);
|
| 528 | return -EINVAL;
|
| 529 | }
|
| 530 |
|
| 531 | if ((orig_start >= EXT_MAX_BLOCKS) ||
|
| 532 | (donor_start >= EXT_MAX_BLOCKS) ||
|
| 533 | (*len > EXT_MAX_BLOCKS) ||
|
| 534 | (donor_start + *len >= EXT_MAX_BLOCKS) ||
|
| 535 | (orig_start + *len >= EXT_MAX_BLOCKS)) {
|
| 536 | ext4_debug("ext4 move extent: Can't handle over [%u] blocks [ino:orig %llu, donor %llu]\n",
|
| 537 | EXT_MAX_BLOCKS,
|
| 538 | orig_inode->i_ino, donor_inode->i_ino);
|
| 539 | return -EINVAL;
|
| 540 | }
|
| 541 |
|
| 542 | orig_eof = EXT4_B_TO_LBLK(orig_inode, i_size_read(orig_inode));
|
| 543 | donor_eof = EXT4_B_TO_LBLK(donor_inode, i_size_read(donor_inode));
|
| 544 | if (orig_eof <= orig_start)
|
| 545 | *len = 0;
|
| 546 | else if (orig_eof < orig_start + *len - 1)
|
| 547 | *len = orig_eof - orig_start;
|
| 548 | if (donor_eof <= donor_start)
|
| 549 | *len = 0;
|
| 550 | else if (donor_eof < donor_start + *len - 1)
|
| 551 | *len = donor_eof - donor_start;
|
| 552 | if (!*len) {
|
| 553 | ext4_debug("ext4 move extent: len should not be 0 [ino:orig %llu, donor %llu]\n",
|
| 554 | orig_inode->i_ino, donor_inode->i_ino);
|
| 555 | return -EINVAL;
|
| 556 | }
|
| 557 |
|
| 558 | return 0;
|
| 559 | }
|
| 560 |
|
| 561 | |
| 562 | * ext4_move_extents - Exchange the specified range of a file
|
| 563 | *
|
| 564 | * @o_filp: file structure of the original file
|
| 565 | * @d_filp: file structure of the donor file
|
| 566 | * @orig_blk: start offset in block for orig
|
| 567 | * @donor_blk: start offset in block for donor
|
| 568 | * @len: the number of blocks to be moved
|
| 569 | * @moved_len: moved block length
|
| 570 | *
|
| 571 | * This function returns 0 and moved block length is set in moved_len
|
| 572 | * if succeed, otherwise returns error value.
|
| 573 |
|
| 574 | int ext4_move_extents(struct file *o_filp, struct file *d_filp, __u64 orig_blk,
|
| 575 | __u64 donor_blk, __u64 len, __u64 *moved_len)
|
| 576 | {
|
| 577 | struct inode *orig_inode = file_inode(o_filp);
|
| 578 | struct inode *donor_inode = file_inode(d_filp);
|
| 579 | struct mext_data mext;
|
| 580 | struct super_block *sb = orig_inode->i_sb;
|
| 581 | struct ext4_sb_info *sbi = EXT4_SB(sb);
|
| 582 | int retries = 0;
|
| 583 | u64 m_len;
|
| 584 | int ret;
|
| 585 |
|
| 586 | *moved_len = 0;
|
| 587 |
|
| 588 |
|
| 589 | lock_two_nondirectories(orig_inode, donor_inode);
|
| 590 |
|
| 591 | ret = mext_check_validity(orig_inode, donor_inode);
|
| 592 | if (ret)
|
| 593 | goto out;
|
| 594 |
|
| 595 |
|
| 596 | inode_dio_wait(orig_inode);
|
| 597 | inode_dio_wait(donor_inode);
|
| 598 |
|
| 599 |
|
| 600 | ret = mext_check_adjust_range(orig_inode, donor_inode, orig_blk,
|
| 601 | donor_blk, &len);
|
| 602 | if (ret)
|
| 603 | goto out;
|
| 604 |
|
| 605 | mext.orig_inode = orig_inode;
|
| 606 | mext.donor_inode = donor_inode;
|
| 607 | while (len) {
|
| 608 | mext.orig_map.m_lblk = orig_blk;
|
| 609 | mext.orig_map.m_len = len;
|
| 610 | mext.orig_map.m_flags = 0;
|
| 611 | mext.donor_lblk = donor_blk;
|
| 612 |
|
| 613 | ret = ext4_map_blocks(NULL, orig_inode, &mext.orig_map, 0);
|
| 614 | if (ret < 0)
|
| 615 | goto out;
|
| 616 |
|
| 617 |
|
| 618 | if (mext.orig_map.m_flags &
|
| 619 | (EXT4_MAP_MAPPED | EXT4_MAP_UNWRITTEN)) {
|
| 620 | ret = mext_move_extent(&mext, &m_len);
|
| 621 | *moved_len += m_len;
|
| 622 | if (!ret)
|
| 623 | goto next;
|
| 624 |
|
| 625 |
|
| 626 | if (m_len) {
|
| 627 | orig_blk += m_len;
|
| 628 | donor_blk += m_len;
|
| 629 | len -= m_len;
|
| 630 | }
|
| 631 | if (ret == -ESTALE)
|
| 632 | continue;
|
| 633 | if (ret == -ENOSPC &&
|
| 634 | ext4_should_retry_alloc(sb, &retries))
|
| 635 | continue;
|
| 636 | if (ret == -EBUSY &&
|
| 637 | sbi->s_journal && retries++ < 4 &&
|
| 638 | jbd2_journal_force_commit_nested(sbi->s_journal))
|
| 639 | continue;
|
| 640 |
|
| 641 | goto out;
|
| 642 | }
|
| 643 | next:
|
| 644 | orig_blk += mext.orig_map.m_len;
|
| 645 | donor_blk += mext.orig_map.m_len;
|
| 646 | len -= mext.orig_map.m_len;
|
| 647 | retries = 0;
|
| 648 | }
|
| 649 |
|
| 650 | out:
|
| 651 | if (*moved_len) {
|
| 652 | ext4_discard_preallocations(orig_inode);
|
| 653 | ext4_discard_preallocations(donor_inode);
|
| 654 | }
|
| 655 |
|
| 656 | unlock_two_nondirectories(orig_inode, donor_inode);
|
| 657 | return ret;
|
| 658 | }
|