Skip to content

Commit f19d587

Browse files
taoma-tmtytso
authored andcommitted
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 <[email protected]> Signed-off-by: "Theodore Ts'o" <[email protected]>
1 parent 46c7f25 commit f19d587

File tree

5 files changed

+340
-42
lines changed

5 files changed

+340
-42
lines changed

fs/ext4/ext4.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2018,8 +2018,19 @@ struct buffer_head *ext4_getblk(handle_t *, struct inode *,
20182018
ext4_lblk_t, int, int *);
20192019
struct buffer_head *ext4_bread(handle_t *, struct inode *,
20202020
ext4_lblk_t, int, int *);
2021+
int ext4_get_block_write(struct inode *inode, sector_t iblock,
2022+
struct buffer_head *bh_result, int create);
20212023
int ext4_get_block(struct inode *inode, sector_t iblock,
20222024
struct buffer_head *bh_result, int create);
2025+
int ext4_walk_page_buffers(handle_t *handle,
2026+
struct buffer_head *head,
2027+
unsigned from,
2028+
unsigned to,
2029+
int *partial,
2030+
int (*fn)(handle_t *handle,
2031+
struct buffer_head *bh));
2032+
int do_journal_get_write_access(handle_t *handle,
2033+
struct buffer_head *bh);
20232034

20242035
extern struct inode *ext4_iget(struct super_block *, unsigned long);
20252036
extern int ext4_write_inode(struct inode *, struct writeback_control *);

fs/ext4/extents.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <linux/fiemap.h>
4343
#include "ext4_jbd2.h"
4444
#include "ext4_extents.h"
45+
#include "xattr.h"
4546

4647
#include <trace/events/ext4.h>
4748

@@ -2310,7 +2311,13 @@ int ext4_ext_calc_credits_for_single_extent(struct inode *inode, int nrblocks,
23102311
int ext4_ext_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
23112312
{
23122313
int index;
2313-
int depth = ext_depth(inode);
2314+
int depth;
2315+
2316+
/* If we are converting the inline data, only one is needed here. */
2317+
if (ext4_has_inline_data(inode))
2318+
return 1;
2319+
2320+
depth = ext_depth(inode);
23142321

23152322
if (chunk)
23162323
index = depth * 2;

fs/ext4/inline.c

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "ext4_jbd2.h"
1515
#include "ext4.h"
1616
#include "xattr.h"
17+
#include "truncate.h"
1718

1819
#define EXT4_XATTR_SYSTEM_DATA "data"
1920
#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)
515516
return ret >= 0 ? 0 : ret;
516517
}
517518

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+
518751
int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
519752
{
520753
int ret;

0 commit comments

Comments
 (0)