Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 377598
b: refs/heads/master
c: 2e8fa54
h: refs/heads/master
v: v3
  • Loading branch information
Jan Kara authored and Theodore Ts'o committed Jun 4, 2013
1 parent b564707 commit e2c9a4e
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 41 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: 6b523df4fb5ae281ddbc817f40504b33e6226554
refs/heads/master: 2e8fa54e3b48e4ce8c4e9ca4674ffbc973f58be5
25 changes: 19 additions & 6 deletions trunk/fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -887,12 +887,22 @@ struct ext4_inode_info {
qsize_t i_reserved_quota;
#endif

/* completed IOs that might need unwritten extents handling */
struct list_head i_completed_io_list;
/* Lock protecting lists below */
spinlock_t i_completed_io_lock;
/*
* Completed IOs that need unwritten extents handling and have
* transaction reserved
*/
struct list_head i_rsv_conversion_list;
/*
* Completed IOs that need unwritten extents handling and don't have
* transaction reserved
*/
struct list_head i_unrsv_conversion_list;
atomic_t i_ioend_count; /* Number of outstanding io_end structs */
atomic_t i_unwritten; /* Nr. of inflight conversions pending */
struct work_struct i_unwritten_work; /* deferred extent conversion */
struct work_struct i_rsv_conversion_work;
struct work_struct i_unrsv_conversion_work;

spinlock_t i_block_reservation_lock;

Expand Down Expand Up @@ -1264,8 +1274,10 @@ struct ext4_sb_info {
struct flex_groups *s_flex_groups;
ext4_group_t s_flex_groups_allocated;

/* workqueue for dio unwritten */
struct workqueue_struct *dio_unwritten_wq;
/* workqueue for unreserved extent convertions (dio) */
struct workqueue_struct *unrsv_conversion_wq;
/* workqueue for reserved extent conversions (buffered io) */
struct workqueue_struct *rsv_conversion_wq;

/* timer for periodic error stats printing */
struct timer_list s_err_report;
Expand Down Expand Up @@ -2646,7 +2658,8 @@ extern int ext4_put_io_end(ext4_io_end_t *io_end);
extern void ext4_put_io_end_defer(ext4_io_end_t *io_end);
extern void ext4_io_submit_init(struct ext4_io_submit *io,
struct writeback_control *wbc);
extern void ext4_end_io_work(struct work_struct *work);
extern void ext4_end_io_rsv_work(struct work_struct *work);
extern void ext4_end_io_unrsv_work(struct work_struct *work);
extern void ext4_io_submit(struct ext4_io_submit *io);
extern int ext4_bio_write_page(struct ext4_io_submit *io,
struct page *page,
Expand Down
65 changes: 42 additions & 23 deletions trunk/fs/ext4/page-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ void ext4_ioend_shutdown(struct inode *inode)
* We need to make sure the work structure is finished being
* used before we let the inode get destroyed.
*/
if (work_pending(&EXT4_I(inode)->i_unwritten_work))
cancel_work_sync(&EXT4_I(inode)->i_unwritten_work);
if (work_pending(&EXT4_I(inode)->i_rsv_conversion_work))
cancel_work_sync(&EXT4_I(inode)->i_rsv_conversion_work);
if (work_pending(&EXT4_I(inode)->i_unrsv_conversion_work))
cancel_work_sync(&EXT4_I(inode)->i_unrsv_conversion_work);
}

static void ext4_release_io_end(ext4_io_end_t *io_end)
Expand Down Expand Up @@ -114,20 +116,17 @@ static int ext4_end_io(ext4_io_end_t *io)
return ret;
}

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

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

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) {
ext4_debug("Dump inode %lu completed io list\n", inode->i_ino);
list_for_each_entry(io, head, list) {
cur = &io->list;
before = cur->prev;
io0 = container_of(before, ext4_io_end_t, list);
Expand All @@ -148,16 +147,23 @@ static void ext4_add_complete_io(ext4_io_end_t *io_end)
unsigned long flags;

BUG_ON(!(io_end->flag & EXT4_IO_END_UNWRITTEN));
wq = EXT4_SB(io_end->inode->i_sb)->dio_unwritten_wq;

spin_lock_irqsave(&ei->i_completed_io_lock, flags);
if (list_empty(&ei->i_completed_io_list))
queue_work(wq, &ei->i_unwritten_work);
list_add_tail(&io_end->list, &ei->i_completed_io_list);
if (io_end->handle) {
wq = EXT4_SB(io_end->inode->i_sb)->rsv_conversion_wq;
if (list_empty(&ei->i_rsv_conversion_list))
queue_work(wq, &ei->i_rsv_conversion_work);
list_add_tail(&io_end->list, &ei->i_rsv_conversion_list);
} else {
wq = EXT4_SB(io_end->inode->i_sb)->unrsv_conversion_wq;
if (list_empty(&ei->i_unrsv_conversion_list))
queue_work(wq, &ei->i_unrsv_conversion_work);
list_add_tail(&io_end->list, &ei->i_unrsv_conversion_list);
}
spin_unlock_irqrestore(&ei->i_completed_io_lock, flags);
}

static int ext4_do_flush_completed_IO(struct inode *inode)
static int ext4_do_flush_completed_IO(struct inode *inode,
struct list_head *head)
{
ext4_io_end_t *io;
struct list_head unwritten;
Expand All @@ -166,8 +172,8 @@ static int ext4_do_flush_completed_IO(struct inode *inode)
int err, ret = 0;

spin_lock_irqsave(&ei->i_completed_io_lock, flags);
dump_completed_IO(inode);
list_replace_init(&ei->i_completed_io_list, &unwritten);
dump_completed_IO(inode, head);
list_replace_init(head, &unwritten);
spin_unlock_irqrestore(&ei->i_completed_io_lock, flags);

while (!list_empty(&unwritten)) {
Expand All @@ -183,21 +189,34 @@ static int ext4_do_flush_completed_IO(struct inode *inode)
}

/*
* work on completed aio dio IO, to convert unwritten extents to extents
* work on completed IO, to convert unwritten extents to extents
*/
void ext4_end_io_work(struct work_struct *work)
void ext4_end_io_rsv_work(struct work_struct *work)
{
struct ext4_inode_info *ei = container_of(work, struct ext4_inode_info,
i_unwritten_work);
ext4_do_flush_completed_IO(&ei->vfs_inode);
i_rsv_conversion_work);
ext4_do_flush_completed_IO(&ei->vfs_inode, &ei->i_rsv_conversion_list);
}

void ext4_end_io_unrsv_work(struct work_struct *work)
{
struct ext4_inode_info *ei = container_of(work, struct ext4_inode_info,
i_unrsv_conversion_work);
ext4_do_flush_completed_IO(&ei->vfs_inode, &ei->i_unrsv_conversion_list);
}

int ext4_flush_unwritten_io(struct inode *inode)
{
int ret;
int ret, err;

WARN_ON_ONCE(!mutex_is_locked(&inode->i_mutex) &&
!(inode->i_state & I_FREEING));
ret = ext4_do_flush_completed_IO(inode);
ret = ext4_do_flush_completed_IO(inode,
&EXT4_I(inode)->i_rsv_conversion_list);
err = ext4_do_flush_completed_IO(inode,
&EXT4_I(inode)->i_unrsv_conversion_list);
if (!ret)
ret = err;
ext4_unwritten_wait(inode);
return ret;
}
Expand Down
38 changes: 27 additions & 11 deletions trunk/fs/ext4/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -750,8 +750,10 @@ static void ext4_put_super(struct super_block *sb)
ext4_unregister_li_request(sb);
dquot_disable(sb, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);

flush_workqueue(sbi->dio_unwritten_wq);
destroy_workqueue(sbi->dio_unwritten_wq);
flush_workqueue(sbi->unrsv_conversion_wq);
flush_workqueue(sbi->rsv_conversion_wq);
destroy_workqueue(sbi->unrsv_conversion_wq);
destroy_workqueue(sbi->rsv_conversion_wq);

if (sbi->s_journal) {
err = jbd2_journal_destroy(sbi->s_journal);
Expand Down Expand Up @@ -859,13 +861,15 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
ei->i_reserved_quota = 0;
#endif
ei->jinode = NULL;
INIT_LIST_HEAD(&ei->i_completed_io_list);
INIT_LIST_HEAD(&ei->i_rsv_conversion_list);
INIT_LIST_HEAD(&ei->i_unrsv_conversion_list);
spin_lock_init(&ei->i_completed_io_lock);
ei->i_sync_tid = 0;
ei->i_datasync_tid = 0;
atomic_set(&ei->i_ioend_count, 0);
atomic_set(&ei->i_unwritten, 0);
INIT_WORK(&ei->i_unwritten_work, ext4_end_io_work);
INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work);
INIT_WORK(&ei->i_unrsv_conversion_work, ext4_end_io_unrsv_work);

return &ei->vfs_inode;
}
Expand Down Expand Up @@ -3936,12 +3940,20 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
* The maximum number of concurrent works can be high and
* concurrency isn't really necessary. Limit it to 1.
*/
EXT4_SB(sb)->dio_unwritten_wq =
alloc_workqueue("ext4-dio-unwritten", WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
if (!EXT4_SB(sb)->dio_unwritten_wq) {
printk(KERN_ERR "EXT4-fs: failed to create DIO workqueue\n");
EXT4_SB(sb)->rsv_conversion_wq =
alloc_workqueue("ext4-rsv-conversion", WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
if (!EXT4_SB(sb)->rsv_conversion_wq) {
printk(KERN_ERR "EXT4-fs: failed to create workqueue\n");
ret = -ENOMEM;
goto failed_mount_wq;
goto failed_mount4;
}

EXT4_SB(sb)->unrsv_conversion_wq =
alloc_workqueue("ext4-unrsv-conversion", WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
if (!EXT4_SB(sb)->unrsv_conversion_wq) {
printk(KERN_ERR "EXT4-fs: failed to create workqueue\n");
ret = -ENOMEM;
goto failed_mount4;
}

/*
Expand Down Expand Up @@ -4095,7 +4107,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
sb->s_root = NULL;
failed_mount4:
ext4_msg(sb, KERN_ERR, "mount failed");
destroy_workqueue(EXT4_SB(sb)->dio_unwritten_wq);
if (EXT4_SB(sb)->rsv_conversion_wq)
destroy_workqueue(EXT4_SB(sb)->rsv_conversion_wq);
if (EXT4_SB(sb)->unrsv_conversion_wq)
destroy_workqueue(EXT4_SB(sb)->unrsv_conversion_wq);
failed_mount_wq:
if (sbi->s_journal) {
jbd2_journal_destroy(sbi->s_journal);
Expand Down Expand Up @@ -4541,7 +4556,8 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
struct ext4_sb_info *sbi = EXT4_SB(sb);

trace_ext4_sync_fs(sb, wait);
flush_workqueue(sbi->dio_unwritten_wq);
flush_workqueue(sbi->rsv_conversion_wq);
flush_workqueue(sbi->unrsv_conversion_wq);
/*
* Writeback quota in non-journalled quota case - journalled quota has
* no dirty dquots
Expand Down

0 comments on commit e2c9a4e

Please sign in to comment.