aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/inline.c
diff options
context:
space:
mode:
authorTao Ma <boyu.mt@taobao.com>2012-12-10 14:05:51 -0500
committerTheodore Ts'o <tytso@mit.edu>2012-12-10 14:05:51 -0500
commitf19d5870cbf72d4cb2a8e1f749dff97af99b071e (patch)
treee919ba96fde504b6d697d1e13abb080d7800f61b /fs/ext4/inline.c
parent46c7f254543dedcf134ad05091ed2b935a9a597d (diff)
ext4: add normal write support for inline data
For a normal write case (not journalled write, not delayed allocation), we write to the inline if the file is small and convert it to an extent based file when the write is larger than the max inline size. Signed-off-by: Tao Ma <boyu.mt@taobao.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4/inline.c')
-rw-r--r--fs/ext4/inline.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index e4a41d5d06db..320ff6fe5d8c 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -14,6 +14,7 @@
14#include "ext4_jbd2.h" 14#include "ext4_jbd2.h"
15#include "ext4.h" 15#include "ext4.h"
16#include "xattr.h" 16#include "xattr.h"
17#include "truncate.h"
17 18
18#define EXT4_XATTR_SYSTEM_DATA "data" 19#define EXT4_XATTR_SYSTEM_DATA "data"
19#define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS)) 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 return ret >= 0 ? 0 : ret; 516 return ret >= 0 ? 0 : ret;
516} 517}
517 518
519static 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
545retry:
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);
617out:
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 */
636int 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;
701out_up_read:
702 up_read(&EXT4_I(inode)->xattr_sem);
703out:
704 if (handle)
705 ext4_journal_stop(handle);
706 brelse(iloc.bh);
707 return ret;
708convert:
709 return ext4_convert_inline_data_to_extent(mapping,
710 inode, flags);
711}
712
713int 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);
746out:
747 return copied;
748}
749
750
518int ext4_destroy_inline_data(handle_t *handle, struct inode *inode) 751int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
519{ 752{
520 int ret; 753 int ret;