|
14 | 14 | #include "ext4_jbd2.h"
|
15 | 15 | #include "ext4.h"
|
16 | 16 | #include "xattr.h"
|
| 17 | +#include "truncate.h" |
17 | 18 |
|
18 | 19 | #define EXT4_XATTR_SYSTEM_DATA "data"
|
19 | 20 | #define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS))
|
@@ -515,6 +516,238 @@ int ext4_readpage_inline(struct inode *inode, struct page *page)
|
515 | 516 | return ret >= 0 ? 0 : ret;
|
516 | 517 | }
|
517 | 518 |
|
| 519 | +static int ext4_convert_inline_data_to_extent(struct address_space *mapping, |
| 520 | + struct inode *inode, |
| 521 | + unsigned flags) |
| 522 | +{ |
| 523 | + int ret, needed_blocks; |
| 524 | + handle_t *handle = NULL; |
| 525 | + int retries = 0, sem_held = 0; |
| 526 | + struct page *page = NULL; |
| 527 | + unsigned from, to; |
| 528 | + struct ext4_iloc iloc; |
| 529 | + |
| 530 | + if (!ext4_has_inline_data(inode)) { |
| 531 | + /* |
| 532 | + * clear the flag so that no new write |
| 533 | + * will trap here again. |
| 534 | + */ |
| 535 | + ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA); |
| 536 | + return 0; |
| 537 | + } |
| 538 | + |
| 539 | + needed_blocks = ext4_writepage_trans_blocks(inode); |
| 540 | + |
| 541 | + ret = ext4_get_inode_loc(inode, &iloc); |
| 542 | + if (ret) |
| 543 | + return ret; |
| 544 | + |
| 545 | +retry: |
| 546 | + handle = ext4_journal_start(inode, needed_blocks); |
| 547 | + if (IS_ERR(handle)) { |
| 548 | + ret = PTR_ERR(handle); |
| 549 | + handle = NULL; |
| 550 | + goto out; |
| 551 | + } |
| 552 | + |
| 553 | + /* We cannot recurse into the filesystem as the transaction is already |
| 554 | + * started */ |
| 555 | + flags |= AOP_FLAG_NOFS; |
| 556 | + |
| 557 | + page = grab_cache_page_write_begin(mapping, 0, flags); |
| 558 | + if (!page) { |
| 559 | + ret = -ENOMEM; |
| 560 | + goto out; |
| 561 | + } |
| 562 | + |
| 563 | + down_write(&EXT4_I(inode)->xattr_sem); |
| 564 | + sem_held = 1; |
| 565 | + /* If some one has already done this for us, just exit. */ |
| 566 | + if (!ext4_has_inline_data(inode)) { |
| 567 | + ret = 0; |
| 568 | + goto out; |
| 569 | + } |
| 570 | + |
| 571 | + from = 0; |
| 572 | + to = ext4_get_inline_size(inode); |
| 573 | + if (!PageUptodate(page)) { |
| 574 | + ret = ext4_read_inline_page(inode, page); |
| 575 | + if (ret < 0) |
| 576 | + goto out; |
| 577 | + } |
| 578 | + |
| 579 | + ret = ext4_destroy_inline_data_nolock(handle, inode); |
| 580 | + if (ret) |
| 581 | + goto out; |
| 582 | + |
| 583 | + if (ext4_should_dioread_nolock(inode)) |
| 584 | + ret = __block_write_begin(page, from, to, ext4_get_block_write); |
| 585 | + else |
| 586 | + ret = __block_write_begin(page, from, to, ext4_get_block); |
| 587 | + |
| 588 | + if (!ret && ext4_should_journal_data(inode)) { |
| 589 | + ret = ext4_walk_page_buffers(handle, page_buffers(page), |
| 590 | + from, to, NULL, |
| 591 | + do_journal_get_write_access); |
| 592 | + } |
| 593 | + |
| 594 | + if (ret) { |
| 595 | + unlock_page(page); |
| 596 | + page_cache_release(page); |
| 597 | + ext4_orphan_add(handle, inode); |
| 598 | + up_write(&EXT4_I(inode)->xattr_sem); |
| 599 | + sem_held = 0; |
| 600 | + ext4_journal_stop(handle); |
| 601 | + handle = NULL; |
| 602 | + ext4_truncate_failed_write(inode); |
| 603 | + /* |
| 604 | + * If truncate failed early the inode might |
| 605 | + * still be on the orphan list; we need to |
| 606 | + * make sure the inode is removed from the |
| 607 | + * orphan list in that case. |
| 608 | + */ |
| 609 | + if (inode->i_nlink) |
| 610 | + ext4_orphan_del(NULL, inode); |
| 611 | + } |
| 612 | + |
| 613 | + if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) |
| 614 | + goto retry; |
| 615 | + |
| 616 | + block_commit_write(page, from, to); |
| 617 | +out: |
| 618 | + if (page) { |
| 619 | + unlock_page(page); |
| 620 | + page_cache_release(page); |
| 621 | + } |
| 622 | + if (sem_held) |
| 623 | + up_write(&EXT4_I(inode)->xattr_sem); |
| 624 | + if (handle) |
| 625 | + ext4_journal_stop(handle); |
| 626 | + brelse(iloc.bh); |
| 627 | + return ret; |
| 628 | +} |
| 629 | + |
| 630 | +/* |
| 631 | + * Try to write data in the inode. |
| 632 | + * If the inode has inline data, check whether the new write can be |
| 633 | + * in the inode also. If not, create the page the handle, move the data |
| 634 | + * to the page make it update and let the later codes create extent for it. |
| 635 | + */ |
| 636 | +int ext4_try_to_write_inline_data(struct address_space *mapping, |
| 637 | + struct inode *inode, |
| 638 | + loff_t pos, unsigned len, |
| 639 | + unsigned flags, |
| 640 | + struct page **pagep) |
| 641 | +{ |
| 642 | + int ret; |
| 643 | + handle_t *handle; |
| 644 | + struct page *page; |
| 645 | + struct ext4_iloc iloc; |
| 646 | + |
| 647 | + if (pos + len > ext4_get_max_inline_size(inode)) |
| 648 | + goto convert; |
| 649 | + |
| 650 | + ret = ext4_get_inode_loc(inode, &iloc); |
| 651 | + if (ret) |
| 652 | + return ret; |
| 653 | + |
| 654 | + /* |
| 655 | + * The possible write could happen in the inode, |
| 656 | + * so try to reserve the space in inode first. |
| 657 | + */ |
| 658 | + handle = ext4_journal_start(inode, 1); |
| 659 | + if (IS_ERR(handle)) { |
| 660 | + ret = PTR_ERR(handle); |
| 661 | + handle = NULL; |
| 662 | + goto out; |
| 663 | + } |
| 664 | + |
| 665 | + ret = ext4_prepare_inline_data(handle, inode, pos + len); |
| 666 | + if (ret && ret != -ENOSPC) |
| 667 | + goto out; |
| 668 | + |
| 669 | + /* We don't have space in inline inode, so convert it to extent. */ |
| 670 | + if (ret == -ENOSPC) { |
| 671 | + ext4_journal_stop(handle); |
| 672 | + brelse(iloc.bh); |
| 673 | + goto convert; |
| 674 | + } |
| 675 | + |
| 676 | + flags |= AOP_FLAG_NOFS; |
| 677 | + |
| 678 | + page = grab_cache_page_write_begin(mapping, 0, flags); |
| 679 | + if (!page) { |
| 680 | + ret = -ENOMEM; |
| 681 | + goto out; |
| 682 | + } |
| 683 | + |
| 684 | + *pagep = page; |
| 685 | + down_read(&EXT4_I(inode)->xattr_sem); |
| 686 | + if (!ext4_has_inline_data(inode)) { |
| 687 | + ret = 0; |
| 688 | + unlock_page(page); |
| 689 | + page_cache_release(page); |
| 690 | + goto out_up_read; |
| 691 | + } |
| 692 | + |
| 693 | + if (!PageUptodate(page)) { |
| 694 | + ret = ext4_read_inline_page(inode, page); |
| 695 | + if (ret < 0) |
| 696 | + goto out_up_read; |
| 697 | + } |
| 698 | + |
| 699 | + ret = 1; |
| 700 | + handle = NULL; |
| 701 | +out_up_read: |
| 702 | + up_read(&EXT4_I(inode)->xattr_sem); |
| 703 | +out: |
| 704 | + if (handle) |
| 705 | + ext4_journal_stop(handle); |
| 706 | + brelse(iloc.bh); |
| 707 | + return ret; |
| 708 | +convert: |
| 709 | + return ext4_convert_inline_data_to_extent(mapping, |
| 710 | + inode, flags); |
| 711 | +} |
| 712 | + |
| 713 | +int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len, |
| 714 | + unsigned copied, struct page *page) |
| 715 | +{ |
| 716 | + int ret; |
| 717 | + void *kaddr; |
| 718 | + struct ext4_iloc iloc; |
| 719 | + |
| 720 | + if (unlikely(copied < len)) { |
| 721 | + if (!PageUptodate(page)) { |
| 722 | + copied = 0; |
| 723 | + goto out; |
| 724 | + } |
| 725 | + } |
| 726 | + |
| 727 | + ret = ext4_get_inode_loc(inode, &iloc); |
| 728 | + if (ret) { |
| 729 | + ext4_std_error(inode->i_sb, ret); |
| 730 | + copied = 0; |
| 731 | + goto out; |
| 732 | + } |
| 733 | + |
| 734 | + down_write(&EXT4_I(inode)->xattr_sem); |
| 735 | + BUG_ON(!ext4_has_inline_data(inode)); |
| 736 | + |
| 737 | + kaddr = kmap_atomic(page); |
| 738 | + ext4_write_inline_data(inode, &iloc, kaddr, pos, len); |
| 739 | + kunmap_atomic(kaddr); |
| 740 | + SetPageUptodate(page); |
| 741 | + /* clear page dirty so that writepages wouldn't work for us. */ |
| 742 | + ClearPageDirty(page); |
| 743 | + |
| 744 | + up_write(&EXT4_I(inode)->xattr_sem); |
| 745 | + brelse(iloc.bh); |
| 746 | +out: |
| 747 | + return copied; |
| 748 | +} |
| 749 | + |
| 750 | + |
518 | 751 | int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
|
519 | 752 | {
|
520 | 753 | int ret;
|
|
0 commit comments