Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 344876
b: refs/heads/master
c: f19d587
h: refs/heads/master
v: v3
  • Loading branch information
Tao Ma authored and Theodore Ts'o committed Dec 10, 2012
1 parent 04b7e0e commit b3a3f72
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 43 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 46c7f254543dedcf134ad05091ed2b935a9a597d
refs/heads/master: f19d5870cbf72d4cb2a8e1f749dff97af99b071e
11 changes: 11 additions & 0 deletions trunk/fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -2018,8 +2018,19 @@ struct buffer_head *ext4_getblk(handle_t *, struct inode *,
ext4_lblk_t, int, int *);
struct buffer_head *ext4_bread(handle_t *, struct inode *,
ext4_lblk_t, int, int *);
int ext4_get_block_write(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create);
int ext4_get_block(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create);
int ext4_walk_page_buffers(handle_t *handle,
struct buffer_head *head,
unsigned from,
unsigned to,
int *partial,
int (*fn)(handle_t *handle,
struct buffer_head *bh));
int do_journal_get_write_access(handle_t *handle,
struct buffer_head *bh);

extern struct inode *ext4_iget(struct super_block *, unsigned long);
extern int ext4_write_inode(struct inode *, struct writeback_control *);
Expand Down
9 changes: 8 additions & 1 deletion trunk/fs/ext4/extents.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include <linux/fiemap.h>
#include "ext4_jbd2.h"
#include "ext4_extents.h"
#include "xattr.h"

#include <trace/events/ext4.h>

Expand Down Expand Up @@ -2310,7 +2311,13 @@ int ext4_ext_calc_credits_for_single_extent(struct inode *inode, int nrblocks,
int ext4_ext_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
{
int index;
int depth = ext_depth(inode);
int depth;

/* If we are converting the inline data, only one is needed here. */
if (ext4_has_inline_data(inode))
return 1;

depth = ext_depth(inode);

if (chunk)
index = depth * 2;
Expand Down
233 changes: 233 additions & 0 deletions trunk/fs/ext4/inline.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "ext4_jbd2.h"
#include "ext4.h"
#include "xattr.h"
#include "truncate.h"

#define EXT4_XATTR_SYSTEM_DATA "data"
#define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS))
Expand Down Expand Up @@ -515,6 +516,238 @@ int ext4_readpage_inline(struct inode *inode, struct page *page)
return ret >= 0 ? 0 : ret;
}

static int ext4_convert_inline_data_to_extent(struct address_space *mapping,
struct inode *inode,
unsigned flags)
{
int ret, needed_blocks;
handle_t *handle = NULL;
int retries = 0, sem_held = 0;
struct page *page = NULL;
unsigned from, to;
struct ext4_iloc iloc;

if (!ext4_has_inline_data(inode)) {
/*
* clear the flag so that no new write
* will trap here again.
*/
ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
return 0;
}

needed_blocks = ext4_writepage_trans_blocks(inode);

ret = ext4_get_inode_loc(inode, &iloc);
if (ret)
return ret;

retry:
handle = ext4_journal_start(inode, needed_blocks);
if (IS_ERR(handle)) {
ret = PTR_ERR(handle);
handle = NULL;
goto out;
}

/* We cannot recurse into the filesystem as the transaction is already
* started */
flags |= AOP_FLAG_NOFS;

page = grab_cache_page_write_begin(mapping, 0, flags);
if (!page) {
ret = -ENOMEM;
goto out;
}

down_write(&EXT4_I(inode)->xattr_sem);
sem_held = 1;
/* If some one has already done this for us, just exit. */
if (!ext4_has_inline_data(inode)) {
ret = 0;
goto out;
}

from = 0;
to = ext4_get_inline_size(inode);
if (!PageUptodate(page)) {
ret = ext4_read_inline_page(inode, page);
if (ret < 0)
goto out;
}

ret = ext4_destroy_inline_data_nolock(handle, inode);
if (ret)
goto out;

if (ext4_should_dioread_nolock(inode))
ret = __block_write_begin(page, from, to, ext4_get_block_write);
else
ret = __block_write_begin(page, from, to, ext4_get_block);

if (!ret && ext4_should_journal_data(inode)) {
ret = ext4_walk_page_buffers(handle, page_buffers(page),
from, to, NULL,
do_journal_get_write_access);
}

if (ret) {
unlock_page(page);
page_cache_release(page);
ext4_orphan_add(handle, inode);
up_write(&EXT4_I(inode)->xattr_sem);
sem_held = 0;
ext4_journal_stop(handle);
handle = NULL;
ext4_truncate_failed_write(inode);
/*
* If truncate failed early the inode might
* still be on the orphan list; we need to
* make sure the inode is removed from the
* orphan list in that case.
*/
if (inode->i_nlink)
ext4_orphan_del(NULL, inode);
}

if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
goto retry;

block_commit_write(page, from, to);
out:
if (page) {
unlock_page(page);
page_cache_release(page);
}
if (sem_held)
up_write(&EXT4_I(inode)->xattr_sem);
if (handle)
ext4_journal_stop(handle);
brelse(iloc.bh);
return ret;
}

/*
* Try to write data in the inode.
* If the inode has inline data, check whether the new write can be
* in the inode also. If not, create the page the handle, move the data
* to the page make it update and let the later codes create extent for it.
*/
int ext4_try_to_write_inline_data(struct address_space *mapping,
struct inode *inode,
loff_t pos, unsigned len,
unsigned flags,
struct page **pagep)
{
int ret;
handle_t *handle;
struct page *page;
struct ext4_iloc iloc;

if (pos + len > ext4_get_max_inline_size(inode))
goto convert;

ret = ext4_get_inode_loc(inode, &iloc);
if (ret)
return ret;

/*
* The possible write could happen in the inode,
* so try to reserve the space in inode first.
*/
handle = ext4_journal_start(inode, 1);
if (IS_ERR(handle)) {
ret = PTR_ERR(handle);
handle = NULL;
goto out;
}

ret = ext4_prepare_inline_data(handle, inode, pos + len);
if (ret && ret != -ENOSPC)
goto out;

/* We don't have space in inline inode, so convert it to extent. */
if (ret == -ENOSPC) {
ext4_journal_stop(handle);
brelse(iloc.bh);
goto convert;
}

flags |= AOP_FLAG_NOFS;

page = grab_cache_page_write_begin(mapping, 0, flags);
if (!page) {
ret = -ENOMEM;
goto out;
}

*pagep = page;
down_read(&EXT4_I(inode)->xattr_sem);
if (!ext4_has_inline_data(inode)) {
ret = 0;
unlock_page(page);
page_cache_release(page);
goto out_up_read;
}

if (!PageUptodate(page)) {
ret = ext4_read_inline_page(inode, page);
if (ret < 0)
goto out_up_read;
}

ret = 1;
handle = NULL;
out_up_read:
up_read(&EXT4_I(inode)->xattr_sem);
out:
if (handle)
ext4_journal_stop(handle);
brelse(iloc.bh);
return ret;
convert:
return ext4_convert_inline_data_to_extent(mapping,
inode, flags);
}

int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
unsigned copied, struct page *page)
{
int ret;
void *kaddr;
struct ext4_iloc iloc;

if (unlikely(copied < len)) {
if (!PageUptodate(page)) {
copied = 0;
goto out;
}
}

ret = ext4_get_inode_loc(inode, &iloc);
if (ret) {
ext4_std_error(inode->i_sb, ret);
copied = 0;
goto out;
}

down_write(&EXT4_I(inode)->xattr_sem);
BUG_ON(!ext4_has_inline_data(inode));

kaddr = kmap_atomic(page);
ext4_write_inline_data(inode, &iloc, kaddr, pos, len);
kunmap_atomic(kaddr);
SetPageUptodate(page);
/* clear page dirty so that writepages wouldn't work for us. */
ClearPageDirty(page);

up_write(&EXT4_I(inode)->xattr_sem);
brelse(iloc.bh);
out:
return copied;
}


int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
{
int ret;
Expand Down
Loading

0 comments on commit b3a3f72

Please sign in to comment.