|
48 | 48 | #include "qgroup.h"
|
49 | 49 | #include "delalloc-space.h"
|
50 | 50 | #include "block-group.h"
|
| 51 | +#include "space-info.h" |
51 | 52 |
|
52 | 53 | struct btrfs_iget_args {
|
53 | 54 | u64 ino;
|
@@ -1354,6 +1355,56 @@ static noinline int csum_exist_in_range(struct btrfs_fs_info *fs_info,
|
1354 | 1355 | return 1;
|
1355 | 1356 | }
|
1356 | 1357 |
|
| 1358 | +static int fallback_to_cow(struct inode *inode, struct page *locked_page, |
| 1359 | + const u64 start, const u64 end, |
| 1360 | + int *page_started, unsigned long *nr_written) |
| 1361 | +{ |
| 1362 | + struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; |
| 1363 | + u64 range_start = start; |
| 1364 | + u64 count; |
| 1365 | + |
| 1366 | + /* |
| 1367 | + * If EXTENT_NORESERVE is set it means that when the buffered write was |
| 1368 | + * made we had not enough available data space and therefore we did not |
| 1369 | + * reserve data space for it, since we though we could do NOCOW for the |
| 1370 | + * respective file range (either there is prealloc extent or the inode |
| 1371 | + * has the NOCOW bit set). |
| 1372 | + * |
| 1373 | + * However when we need to fallback to COW mode (because for example the |
| 1374 | + * block group for the corresponding extent was turned to RO mode by a |
| 1375 | + * scrub or relocation) we need to do the following: |
| 1376 | + * |
| 1377 | + * 1) We increment the bytes_may_use counter of the data space info. |
| 1378 | + * If COW succeeds, it allocates a new data extent and after doing |
| 1379 | + * that it decrements the space info's bytes_may_use counter and |
| 1380 | + * increments its bytes_reserved counter by the same amount (we do |
| 1381 | + * this at btrfs_add_reserved_bytes()). So we need to increment the |
| 1382 | + * bytes_may_use counter to compensate (when space is reserved at |
| 1383 | + * buffered write time, the bytes_may_use counter is incremented); |
| 1384 | + * |
| 1385 | + * 2) We clear the EXTENT_NORESERVE bit from the range. We do this so |
| 1386 | + * that if the COW path fails for any reason, it decrements (through |
| 1387 | + * extent_clear_unlock_delalloc()) the bytes_may_use counter of the |
| 1388 | + * data space info, which we incremented in the step above. |
| 1389 | + */ |
| 1390 | + count = count_range_bits(io_tree, &range_start, end, end + 1 - start, |
| 1391 | + EXTENT_NORESERVE, 0); |
| 1392 | + if (count > 0) { |
| 1393 | + struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; |
| 1394 | + struct btrfs_space_info *sinfo = fs_info->data_sinfo; |
| 1395 | + |
| 1396 | + spin_lock(&sinfo->lock); |
| 1397 | + btrfs_space_info_update_bytes_may_use(fs_info, sinfo, count); |
| 1398 | + spin_unlock(&sinfo->lock); |
| 1399 | + |
| 1400 | + clear_extent_bit(io_tree, start, end, EXTENT_NORESERVE, 0, 0, |
| 1401 | + NULL); |
| 1402 | + } |
| 1403 | + |
| 1404 | + return cow_file_range(inode, locked_page, start, end, page_started, |
| 1405 | + nr_written, 1); |
| 1406 | +} |
| 1407 | + |
1357 | 1408 | /*
|
1358 | 1409 | * when nowcow writeback call back. This checks for snapshots or COW copies
|
1359 | 1410 | * of the extents that exist in the file, and COWs the file as required.
|
@@ -1601,9 +1652,9 @@ static noinline int run_delalloc_nocow(struct inode *inode,
|
1601 | 1652 | * NOCOW, following one which needs to be COW'ed
|
1602 | 1653 | */
|
1603 | 1654 | if (cow_start != (u64)-1) {
|
1604 |
| - ret = cow_file_range(inode, locked_page, |
1605 |
| - cow_start, found_key.offset - 1, |
1606 |
| - page_started, nr_written, 1); |
| 1655 | + ret = fallback_to_cow(inode, locked_page, cow_start, |
| 1656 | + found_key.offset - 1, |
| 1657 | + page_started, nr_written); |
1607 | 1658 | if (ret) {
|
1608 | 1659 | if (nocow)
|
1609 | 1660 | btrfs_dec_nocow_writers(fs_info,
|
@@ -1692,8 +1743,8 @@ static noinline int run_delalloc_nocow(struct inode *inode,
|
1692 | 1743 |
|
1693 | 1744 | if (cow_start != (u64)-1) {
|
1694 | 1745 | cur_offset = end;
|
1695 |
| - ret = cow_file_range(inode, locked_page, cow_start, end, |
1696 |
| - page_started, nr_written, 1); |
| 1746 | + ret = fallback_to_cow(inode, locked_page, cow_start, end, |
| 1747 | + page_started, nr_written); |
1697 | 1748 | if (ret)
|
1698 | 1749 | goto error;
|
1699 | 1750 | }
|
|
0 commit comments