Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 185873
b: refs/heads/master
c: c7064ef
h: refs/heads/master
i:
  185871: 2f9149b
v: v3
  • Loading branch information
Jiaying Zhang authored and Theodore Ts'o committed Mar 2, 2010
1 parent 719aaaf commit e3a0beb
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 70 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: b8b8afe236e97b6359d46d3a3f8c46455e192271
refs/heads/master: c7064ef13b2181a489836349f9baf87df0dab28f
18 changes: 9 additions & 9 deletions trunk/fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ struct mpage_da_data {
int pages_written;
int retval;
};
#define DIO_AIO_UNWRITTEN 0x1
#define EXT4_IO_UNWRITTEN 0x1
typedef struct ext4_io_end {
struct list_head list; /* per-file finished AIO list */
struct inode *inode; /* file being written to */
Expand Down Expand Up @@ -355,13 +355,13 @@ struct ext4_new_group_data {
/* caller is from the direct IO path, request to creation of an
unitialized extents if not allocated, split the uninitialized
extent if blocks has been preallocated already*/
#define EXT4_GET_BLOCKS_DIO 0x0008
#define EXT4_GET_BLOCKS_PRE_IO 0x0008
#define EXT4_GET_BLOCKS_CONVERT 0x0010
#define EXT4_GET_BLOCKS_DIO_CREATE_EXT (EXT4_GET_BLOCKS_DIO|\
#define EXT4_GET_BLOCKS_IO_CREATE_EXT (EXT4_GET_BLOCKS_PRE_IO|\
EXT4_GET_BLOCKS_CREATE_UNINIT_EXT)
/* Convert extent to initialized after direct IO complete */
#define EXT4_GET_BLOCKS_DIO_CONVERT_EXT (EXT4_GET_BLOCKS_CONVERT|\
EXT4_GET_BLOCKS_DIO_CREATE_EXT)
/* Convert extent to initialized after IO complete */
#define EXT4_GET_BLOCKS_IO_CONVERT_EXT (EXT4_GET_BLOCKS_CONVERT|\
EXT4_GET_BLOCKS_IO_CREATE_EXT)

/*
* Flags used by ext4_free_blocks
Expand Down Expand Up @@ -700,8 +700,8 @@ struct ext4_inode_info {
qsize_t i_reserved_quota;
#endif

/* completed async DIOs that might need unwritten extents handling */
struct list_head i_aio_dio_complete_list;
/* completed IOs that might need unwritten extents handling */
struct list_head i_completed_io_list;
/* current io_end structure for async DIO write*/
ext4_io_end_t *cur_aio_dio;

Expand Down Expand Up @@ -1461,7 +1461,7 @@ extern int ext4_block_truncate_page(handle_t *handle,
struct address_space *mapping, loff_t from);
extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
extern qsize_t *ext4_get_reserved_space(struct inode *inode);
extern int flush_aio_dio_completed_IO(struct inode *inode);
extern int flush_completed_IO(struct inode *inode);
extern void ext4_da_update_reserve_space(struct inode *inode,
int used, int quota_claim);
/* ioctl.c */
Expand Down
24 changes: 12 additions & 12 deletions trunk/fs/ext4/extents.c
Original file line number Diff line number Diff line change
Expand Up @@ -1619,7 +1619,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
BUG_ON(path[depth].p_hdr == NULL);

/* try to insert block into found extent and return */
if (ex && (flag != EXT4_GET_BLOCKS_DIO_CREATE_EXT)
if (ex && (flag != EXT4_GET_BLOCKS_PRE_IO)
&& ext4_can_extents_be_merged(inode, ex, newext)) {
ext_debug("append [%d]%d block to %d:[%d]%d (from %llu)\n",
ext4_ext_is_uninitialized(newext),
Expand Down Expand Up @@ -1740,7 +1740,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,

merge:
/* try to merge extents to the right */
if (flag != EXT4_GET_BLOCKS_DIO_CREATE_EXT)
if (flag != EXT4_GET_BLOCKS_PRE_IO)
ext4_ext_try_to_merge(inode, path, nearex);

/* try to merge extents to the left */
Expand Down Expand Up @@ -2984,7 +2984,7 @@ static int ext4_split_unwritten_extents(handle_t *handle,
ext4_ext_dirty(handle, inode, path + depth);
return err;
}
static int ext4_convert_unwritten_extents_dio(handle_t *handle,
static int ext4_convert_unwritten_extents_endio(handle_t *handle,
struct inode *inode,
struct ext4_ext_path *path)
{
Expand Down Expand Up @@ -3064,8 +3064,8 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
flags, allocated);
ext4_ext_show_leaf(inode, path);

/* DIO get_block() before submit the IO, split the extent */
if (flags == EXT4_GET_BLOCKS_DIO_CREATE_EXT) {
/* get_block() before submit the IO, split the extent */
if (flags == EXT4_GET_BLOCKS_PRE_IO) {
ret = ext4_split_unwritten_extents(handle,
inode, path, iblock,
max_blocks, flags);
Expand All @@ -3075,14 +3075,14 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
* completed
*/
if (io)
io->flag = DIO_AIO_UNWRITTEN;
io->flag = EXT4_IO_UNWRITTEN;
else
ext4_set_inode_state(inode, EXT4_STATE_DIO_UNWRITTEN);
goto out;
}
/* async DIO end_io complete, convert the filled extent to written */
if (flags == EXT4_GET_BLOCKS_DIO_CONVERT_EXT) {
ret = ext4_convert_unwritten_extents_dio(handle, inode,
/* IO end_io complete, convert the filled extent to written */
if (flags == EXT4_GET_BLOCKS_CONVERT) {
ret = ext4_convert_unwritten_extents_endio(handle, inode,
path);
if (ret >= 0)
ext4_update_inode_fsync_trans(handle, inode, 1);
Expand Down Expand Up @@ -3359,9 +3359,9 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode,
* For non asycn direct IO case, flag the inode state
* that we need to perform convertion when IO is done.
*/
if (flags == EXT4_GET_BLOCKS_DIO_CREATE_EXT) {
if (flags == EXT4_GET_BLOCKS_PRE_IO) {
if (io)
io->flag = DIO_AIO_UNWRITTEN;
io->flag = EXT4_IO_UNWRITTEN;
else
ext4_set_inode_state(inode,
EXT4_STATE_DIO_UNWRITTEN);
Expand Down Expand Up @@ -3656,7 +3656,7 @@ int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset,
map_bh.b_state = 0;
ret = ext4_get_blocks(handle, inode, block,
max_blocks, &map_bh,
EXT4_GET_BLOCKS_DIO_CONVERT_EXT);
EXT4_GET_BLOCKS_IO_CONVERT_EXT);
if (ret <= 0) {
WARN_ON(ret <= 0);
printk(KERN_ERR "%s: ext4_ext_get_blocks "
Expand Down
2 changes: 1 addition & 1 deletion trunk/fs/ext4/fsync.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ int ext4_sync_file(struct file *file, struct dentry *dentry, int datasync)
if (inode->i_sb->s_flags & MS_RDONLY)
return 0;

ret = flush_aio_dio_completed_IO(inode);
ret = flush_completed_IO(inode);
if (ret < 0)
return ret;

Expand Down
84 changes: 38 additions & 46 deletions trunk/fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -3468,36 +3468,22 @@ static ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb,
return ret;
}

static int ext4_get_block_dio_write(struct inode *inode, sector_t iblock,
static int ext4_get_block_write(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create)
{
handle_t *handle = NULL;
int ret = 0;
unsigned max_blocks = bh_result->b_size >> inode->i_blkbits;
int dio_credits;

ext4_debug("ext4_get_block_dio_write: inode %lu, create flag %d\n",
ext4_debug("ext4_get_block_write: inode %lu, create flag %d\n",
inode->i_ino, create);
/*
* DIO VFS code passes create = 0 flag for write to
* the middle of file. It does this to avoid block
* allocation for holes, to prevent expose stale data
* out when there is parallel buffered read (which does
* not hold the i_mutex lock) while direct IO write has
* not completed. DIO request on holes finally falls back
* to buffered IO for this reason.
*
* For ext4 extent based file, since we support fallocate,
* new allocated extent as uninitialized, for holes, we
* could fallocate blocks for holes, thus parallel
* buffered IO read will zero out the page when read on
* a hole while parallel DIO write to the hole has not completed.
*
* when we come here, we know it's a direct IO write to
* to the middle of file (<i_size)
* so it's safe to override the create flag from VFS.
* ext4_get_block in prepare for a DIO write or buffer write.
* We allocate an uinitialized extent if blocks haven't been allocated.
* The extent will be converted to initialized after IO complete.
*/
create = EXT4_GET_BLOCKS_DIO_CREATE_EXT;
create = EXT4_GET_BLOCKS_IO_CREATE_EXT;

if (max_blocks > DIO_MAX_BLOCKS)
max_blocks = DIO_MAX_BLOCKS;
Expand All @@ -3524,19 +3510,20 @@ static void ext4_free_io_end(ext4_io_end_t *io)
iput(io->inode);
kfree(io);
}
static void dump_aio_dio_list(struct inode * inode)

static void dump_completed_IO(struct inode * inode)
{
#ifdef EXT4_DEBUG
struct list_head *cur, *before, *after;
ext4_io_end_t *io, *io0, *io1;

if (list_empty(&EXT4_I(inode)->i_aio_dio_complete_list)){
ext4_debug("inode %lu aio dio list is empty\n", inode->i_ino);
if (list_empty(&EXT4_I(inode)->i_completed_io_list)){
ext4_debug("inode %lu completed_io list is empty\n", inode->i_ino);
return;
}

ext4_debug("Dump inode %lu aio_dio_completed_IO list \n", inode->i_ino);
list_for_each_entry(io, &EXT4_I(inode)->i_aio_dio_complete_list, list){
ext4_debug("Dump inode %lu completed_io list \n", inode->i_ino);
list_for_each_entry(io, &EXT4_I(inode)->i_completed_io_list, list){
cur = &io->list;
before = cur->prev;
io0 = container_of(before, ext4_io_end_t, list);
Expand All @@ -3552,21 +3539,21 @@ static void dump_aio_dio_list(struct inode * inode)
/*
* check a range of space and convert unwritten extents to written.
*/
static int ext4_end_aio_dio_nolock(ext4_io_end_t *io)
static int ext4_end_io_nolock(ext4_io_end_t *io)
{
struct inode *inode = io->inode;
loff_t offset = io->offset;
ssize_t size = io->size;
int ret = 0;

ext4_debug("end_aio_dio_onlock: io 0x%p from inode %lu,list->next 0x%p,"
ext4_debug("ext4_end_io_nolock: io 0x%p from inode %lu,list->next 0x%p,"
"list->prev 0x%p\n",
io, inode->i_ino, io->list.next, io->list.prev);

if (list_empty(&io->list))
return ret;

if (io->flag != DIO_AIO_UNWRITTEN)
if (io->flag != EXT4_IO_UNWRITTEN)
return ret;

if (offset + size <= i_size_read(inode))
Expand All @@ -3584,50 +3571,54 @@ static int ext4_end_aio_dio_nolock(ext4_io_end_t *io)
io->flag = 0;
return ret;
}

/*
* work on completed aio dio IO, to convert unwritten extents to extents
*/
static void ext4_end_aio_dio_work(struct work_struct *work)
static void ext4_end_io_work(struct work_struct *work)
{
ext4_io_end_t *io = container_of(work, ext4_io_end_t, work);
struct inode *inode = io->inode;
int ret = 0;

mutex_lock(&inode->i_mutex);
ret = ext4_end_aio_dio_nolock(io);
ret = ext4_end_io_nolock(io);
if (ret >= 0) {
if (!list_empty(&io->list))
list_del_init(&io->list);
ext4_free_io_end(io);
}
mutex_unlock(&inode->i_mutex);
}

/*
* This function is called from ext4_sync_file().
*
* When AIO DIO IO is completed, the work to convert unwritten
* extents to written is queued on workqueue but may not get immediately
* When IO is completed, the work to convert unwritten extents to
* written is queued on workqueue but may not get immediately
* scheduled. When fsync is called, we need to ensure the
* conversion is complete before fsync returns.
* The inode keeps track of a list of completed AIO from DIO path
* that might needs to do the conversion. This function walks through
* the list and convert the related unwritten extents to written.
* The inode keeps track of a list of pending/completed IO that
* might needs to do the conversion. This function walks through
* the list and convert the related unwritten extents for completed IO
* to written.
* The function return the number of pending IOs on success.
*/
int flush_aio_dio_completed_IO(struct inode *inode)
int flush_completed_IO(struct inode *inode)
{
ext4_io_end_t *io;
int ret = 0;
int ret2 = 0;

if (list_empty(&EXT4_I(inode)->i_aio_dio_complete_list))
if (list_empty(&EXT4_I(inode)->i_completed_io_list))
return ret;

dump_aio_dio_list(inode);
while (!list_empty(&EXT4_I(inode)->i_aio_dio_complete_list)){
io = list_entry(EXT4_I(inode)->i_aio_dio_complete_list.next,
dump_completed_IO(inode);
while (!list_empty(&EXT4_I(inode)->i_completed_io_list)){
io = list_entry(EXT4_I(inode)->i_completed_io_list.next,
ext4_io_end_t, list);
/*
* Calling ext4_end_aio_dio_nolock() to convert completed
* Calling ext4_end_io_nolock() to convert completed
* IO to written.
*
* When ext4_sync_file() is called, run_queue() may already
Expand All @@ -3640,7 +3631,7 @@ int flush_aio_dio_completed_IO(struct inode *inode)
* avoid double converting from both fsync and background work
* queue work.
*/
ret = ext4_end_aio_dio_nolock(io);
ret = ext4_end_io_nolock(io);
if (ret < 0)
ret2 = ret;
else
Expand All @@ -3662,7 +3653,7 @@ static ext4_io_end_t *ext4_init_io_end (struct inode *inode)
io->offset = 0;
io->size = 0;
io->error = 0;
INIT_WORK(&io->work, ext4_end_aio_dio_work);
INIT_WORK(&io->work, ext4_end_io_work);
INIT_LIST_HEAD(&io->list);
}

Expand All @@ -3685,7 +3676,7 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset,
size);

/* if not aio dio with unwritten extents, just free io and return */
if (io_end->flag != DIO_AIO_UNWRITTEN){
if (io_end->flag != EXT4_IO_UNWRITTEN){
ext4_free_io_end(io_end);
iocb->private = NULL;
return;
Expand All @@ -3700,9 +3691,10 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset,

/* Add the io_end to per-inode completed aio dio list*/
list_add_tail(&io_end->list,
&EXT4_I(io_end->inode)->i_aio_dio_complete_list);
&EXT4_I(io_end->inode)->i_completed_io_list);
iocb->private = NULL;
}

/*
* For ext4 extent files, ext4 will do direct-io write to holes,
* preallocated extents, and those write extend the file, no need to
Expand Down Expand Up @@ -3772,7 +3764,7 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
ret = blockdev_direct_IO(rw, iocb, inode,
inode->i_sb->s_bdev, iov,
offset, nr_segs,
ext4_get_block_dio_write,
ext4_get_block_write,
ext4_end_io_dio);
if (iocb->private)
EXT4_I(inode)->cur_aio_dio = NULL;
Expand Down
2 changes: 1 addition & 1 deletion trunk/fs/ext4/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
#ifdef CONFIG_QUOTA
ei->i_reserved_quota = 0;
#endif
INIT_LIST_HEAD(&ei->i_aio_dio_complete_list);
INIT_LIST_HEAD(&ei->i_completed_io_list);
ei->cur_aio_dio = NULL;
ei->i_sync_tid = 0;
ei->i_datasync_tid = 0;
Expand Down

0 comments on commit e3a0beb

Please sign in to comment.