Kill async inode writeback
cover Kill async inode writeback
Currently we do background inode writeback on demand from many different places - xfssyncd, xfsbufd, the bdi writeback threads and when pushing the AIL. The result is that inodes can be pushed at any time and there is little to no locality to the IO patterns results from such writeback. Indeed, we can have completing writebacks occurring which only serves to slow down writeback.
The idea behind this series is to make metadata buffers get written from xfsbufd via the delayed write queue rather than than from all these other places. All the other places do is make the buffers delayed write so that the xfsbufd can issue them.
This means that inode flushes can no longer happen asynchronously, but we still need a method for ensuring timely dispatch of buffers that we may be waiting for IO completion on. To do this, we allow delayed write buffers to be "promoted" in the delayed write queue. This effectively short-cuts the aging of the buffers, and combined with a demand flush of the xfsbufd we push all aged and promoted buffers out at the same time.
Combine this with sorting the delayed write buffers to be written into disk offset order before dispatch, and we vastly improve the IO patterns for metadata writeback. IO is issued from one place and in a disk/elevator friendly order.
Perf results on a debug XFS build (means allocation patterns are variable, so runtimes are also a bit variable):
Untar 2.6.32 kernel tarball, sync, then remove:
Untar+sync rm -rf
xfs-dev: 25.2s 13.0s
xfs-dev-delwri: 22.5s 9.1s
4 processes each creating 100,000, five byte files in separate directories concurrently, then 4 processes removing a directory each concurrently.
create rm -rf
xfs-dev: 8m32s 4m10s
xfs-dev-delwri: 4m55s 3m42s
There is still followup work to be done on the buffer sorting to make it more efficient, but overall the concept appears to be solid based on the improvements in sustained small file create rates.
_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
PATCH 1/3 XFS: Use delayed write for inodes rather than async
We currently do background inode flush asynchronously, resulting in inodes being written in whatever order the background writeback issues them.
Make the inode flush delayed write instead of asynchronous which will push the inode into the backing buffer but not issue any IO. The buffer will then sit in cache to be flushed by either an AIL push or the xfsbufd timing out the buffer. This will allow accumulation of dirty inode buffers in memory and allow optimisation of inode cluster writeback at the xfsbufd level where we have much greater queue depths than the block layer elevators.
This effectively means that any inode that is written back by background writeback will be seen as flush locked during AIL pushing, and will result in the buffers being pushed from there. A future path will address this non-optimal form of writeback, too.
A side effect of this delayed write mechanism is that background inode reclaim will no longer directly flush inodes, nor can it wait on the flush lock. The result is that inode reclaim must leave the inode in the reclaimable state until it is clean. Hence attempts to reclaim a dirty inode in the background will simply skip the inode until it is clean and this allows other mechanisms (i.e. xfsbufd) to do more optimal writeback of the dirty buffers.
--- fs/xfs/linux-2.6/xfs_super.c | 2 +- fs/xfs/linux-2.6/xfs_sync.c | 52 +++++++++++++++++++++++++++++------------- fs/xfs/xfs_inode.c | 31 ++++--------------------- fs/xfs/xfs_inode.h | 8 ++---- fs/xfs/xfs_inode_item.c | 10 +++++-- fs/xfs/xfs_mount.c | 3 +- 6 files changed, 54 insertions(+), 52 deletions(-)
fs/xfs/linux-2.6/xfs_super.c +1 -1
@@ -1074,7 +1074,7 @@ xfs_fs_write_inode(if (xfs_ipincount (ip) || !xfs_iflock_nowait (ip)) goto out_unlock; - error =xfs_iflush (ip,XFS_IFLUSH_ASYNC_NOBLOCK ); + error =xfs_iflush (ip,XFS_IFLUSH_DELWRI_NOBLOCK ); } out_unlock:
fs/xfs/linux-2.6/xfs_sync.c +36 -16
90 lines not on this page · raw message
fs/xfs/xfs_inode.c +5 -26
@@ -2832,7 +2832,7 @@ xfs_iflush(xfs_dinode_t *dip;xfs_mount_t *mp;int error; -int noblock = (flags ==XFS_IFLUSH_ASYNC_NOBLOCK ); +int noblock = (flags ==XFS_IFLUSH_DELWRI_NOBLOCK );enum {INT_DELWRI = (1 <<0 ),INT_ASYNC = (1 <<1 ) };XFS_STATS_INC (xs_iflush_count);
@@ -2905,12 +2905,8 @@ xfs_iflush(case XFS_IFLUSH_DELWRI_ELSE_SYNC : flags =0 ;break ; -case XFS_IFLUSH_ASYNC_NOBLOCK : -case XFS_IFLUSH_ASYNC : -case XFS_IFLUSH_DELWRI_ELSE_ASYNC : - flags =INT_ASYNC ; -break ;case XFS_IFLUSH_DELWRI : +case XFS_IFLUSH_DELWRI_NOBLOCK : flags =INT_DELWRI ;break ;default :
@@ -2920,15 +2916,11 @@ xfs_iflush( } }else {switch (flags) { +case XFS_IFLUSH_DELWRI_NOBLOCK :case XFS_IFLUSH_DELWRI_ELSE_SYNC : -case XFS_IFLUSH_DELWRI_ELSE_ASYNC :case XFS_IFLUSH_DELWRI : flags =INT_DELWRI ;break ; -case XFS_IFLUSH_ASYNC_NOBLOCK : -case XFS_IFLUSH_ASYNC : - flags =INT_ASYNC ; -break ;case XFS_IFLUSH_SYNC : flags =0 ;break ;
@@ -2971,13 +2963,10 @@ xfs_iflush(if (error) goto cluster_corrupt_out; -if (flags &INT_DELWRI ) { +if (flags &INT_DELWRI )xfs_bdwrite (mp, bp); - }else if (flags &INT_ASYNC ) { - error =xfs_bawrite (mp, bp); - }else { +else error =xfs_bwrite (mp, bp); - }return error; corrupt_out:
@@ -3012,16 +3001,6 @@ xfs_iflush_int( iip = ip->i_itemp; mp = ip->i_mount; - -/* -* If the inode isn't dirty, then just release the inode -* flush lock and do nothing. -*/ -if (xfs_inode_clean (ip)) { -xfs_ifunlock (ip); -return 0 ; - } -/* set *dip = inode's place in the buffer */ dip = (xfs_dinode_t *)xfs_buf_offset (bp, ip->i_imap.im_boffset);
fs/xfs/xfs_inode.h +3 -5
@@ -423,11 +423,9 @@ static inline void xfs_ifunlock(xfs_inode_t *ip)* Flags for xfs_iflush() */ #define XFS_IFLUSH_DELWRI_ELSE_SYNC 1 -#define XFS_IFLUSH_DELWRI_ELSE_ASYNC 2 -#define XFS_IFLUSH_SYNC 3 -#define XFS_IFLUSH_ASYNC 4 -#define XFS_IFLUSH_DELWRI 5 -#define XFS_IFLUSH_ASYNC_NOBLOCK 6 +#define XFS_IFLUSH_SYNC 2 +#define XFS_IFLUSH_DELWRI 3 +#define XFS_IFLUSH_DELWRI_NOBLOCK 4/* * Flags for xfs_itruncate_start().
fs/xfs/xfs_inode_item.c +7 -3
@@ -867,10 +867,14 @@ xfs_inode_item_push( iip->ili_format.ilf_fields != 0);/* -* Write out the inode. The completion routine ('iflush_done') will -* pull it from the AIL, mark it clean, unlock the flush lock. +* Push the inode to it's backing buffer. This will not remove +* the inode from the AIL - a further push will be required to trigger +* a buffer push. However, this allows all the dirty inodes to be pushed to +* the buffer before it is pushed to disk. THe buffer IO completion +* will pull th einode from the AIL, mark it clean and unlock the flush +* lock. */ - (void )xfs_iflush (ip,XFS_IFLUSH_ASYNC ); + (void )xfs_iflush (ip,XFS_IFLUSH_DELWRI );xfs_iunlock (ip,XFS_ILOCK_SHARED );return ;
fs/xfs/xfs_mount.c +2 -1
@@ -1444,7 +1444,8 @@ xfs_unmountfs(* need to force the log first. */ xfs_log_force (mp, (xfs_lsn_t )0 ,XFS_LOG_FORCE |XFS_LOG_SYNC ); -xfs_reclaim_inodes (mp,XFS_IFLUSH_ASYNC ); +xfs_reclaim_inodes (mp,XFS_IFLUSH_DELWRI ); +XFS_bflush (mp->m_ddev_targp);xfs_qm_unmount (mp);
Signature · 6 lines
1.6.5 _______________________________________________ xfs mailing list [email protected] http://oss.sgi.com/mailman/listinfo/xfs
PATCH 2/3 XFS: Don't issue buffer IO direct from AIL push
In reply to Dave ChinnerAll buffers logged into the AIL are marked as delayed write. When the AIL needs to push the buffer out, it issues an async write of the buffer. This means that IO patterns are dependent on the order of buffers in the AIL.
Instead of flushing the buffer, promote the buffer in the delayed write list so that the next time the xfsbufd is run the buffer will be flushed by the xfsbufd. Return the state to the xfsaild that the buffer was promoted so that the xfsaild knows that it needs to cause the xfsbufd to run to flush the buffers that were promoted.
Using the xfsbufd for issuing the IO allows us to dispatch all buffer IO from the one queue. This means that we can make much more enlightened decisions on what order to flush buffers to disk as we don't have multiple places issuing IO. Optimisations to xfsbufd will be in a future patch.
--- fs/xfs/linux-2.6/xfs_buf.c | 22 +++++++++ fs/xfs/linux-2.6/xfs_buf.h | 11 +++++ fs/xfs/linux-2.6/xfs_trace.h | 1 + fs/xfs/quota/xfs_dquot_item.c | 87 +++++------------------------------ fs/xfs/quota/xfs_dquot_item.h | 4 -- fs/xfs/xfs_buf_item.c | 64 ++++++++++++++------------ fs/xfs/xfs_inode_item.c | 99 ++++++---------------------------------- fs/xfs/xfs_inode_item.h | 6 --- fs/xfs/xfs_trans_ail.c | 7 +++ 9 files changed, 104 insertions(+), 197 deletions(-)
fs/xfs/linux-2.6/xfs_buf.c +22 -0
@@ -1631,6 +1631,28 @@ xfs_buf_delwri_dequeue(trace_xfs_buf_delwri_dequeue (bp, _RET_IP_); } +/* +* If a delwri buffer needs to be pushed before it has aged out, then +* promote it to the head of the delwri queue so that it will be flushed +* on the next xfsbufd run. +*/ +void +xfs_buf_delwri_promote ( +xfs_buf_t *bp) +{ +struct list_head *dwq = &bp->b_target->bt_delwrite_queue; +spinlock_t *dwlk = &bp->b_target->bt_delwrite_lock; +long age = xfs_buf_age_centisecs *msecs_to_jiffies (10 ) +1 ; + +spin_lock (dwlk); +ASSERT (bp->b_flags &XBF_DELWRI ); +ASSERT (bp->b_flags & _XBF_DELWRI_Q); +list_del (&bp->b_list); +list_add (&bp->b_list, dwq); + bp->b_queuetime = jiffies - age; +spin_unlock (dwlk); +} + STATIC voidxfs_buf_runall_queues (struct workqueue_struct *queue)
fs/xfs/linux-2.6/xfs_buf.h +11 -0
@@ -261,6 +261,7 @@ extern int xfs_buf_ispin(xfs_buf_t *);/* Delayed Write Buffer Routines */ extern void xfs_buf_delwri_dequeue (xfs_buf_t *); +extern void xfs_buf_delwri_promote (xfs_buf_t *);/* Buffer Daemon Setup Routines */ extern int xfs_buf_init (void );
@@ -424,6 +425,16 @@ extern void xfs_free_buftarg(struct xfs_mount *, struct xfs_buftarg *);extern void xfs_wait_buftarg (xfs_buftarg_t *);extern int xfs_setsize_buftarg (xfs_buftarg_t *,unsigned int ,unsigned int );extern int xfs_flush_buftarg (xfs_buftarg_t *,int ); + +/* +* run the xfsbufd on demand to age buffers. Use in combination with +* xfs_buf_delwri_promote() to flus delayed write buffers efficiently. +*/ +static inline void xfs_flush_buftarg_delwri (xfs_buftarg_t *btp) +{ +wake_up_process (btp->bt_task); +} +#ifdef CONFIG_KDB_MODULES extern struct list_head *xfs_get_buftarg_list (void );#endif
fs/xfs/linux-2.6/xfs_trace.h +1 -0
@@ -464,6 +464,7 @@ DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unlock);DEFINE_BUF_ITEM_EVENT (xfs_buf_item_unlock_stale);DEFINE_BUF_ITEM_EVENT (xfs_buf_item_committed);DEFINE_BUF_ITEM_EVENT (xfs_buf_item_push); +DEFINE_BUF_ITEM_EVENT (xfs_buf_item_pushbuf);DEFINE_BUF_ITEM_EVENT (xfs_trans_get_buf);DEFINE_BUF_ITEM_EVENT (xfs_trans_get_buf_recur);DEFINE_BUF_ITEM_EVENT (xfs_trans_getsb);
fs/xfs/quota/xfs_dquot_item.c +13 -74
131 lines not on this page · raw message
fs/xfs/quota/xfs_dquot_item.h +0 -4
@@ -27,10 +27,6 @@ typedef struct xfs_dq_logitem {xfs_log_item_t qli_item;/* common portion */ struct xfs_dquot *qli_dquot;/* dquot ptr */ xfs_lsn_t qli_flush_lsn;/* lsn at last flush */ -unsigned short qli_pushbuf_flag;/* 1 bit used in push_ail */ -#ifdef DEBUG -uint64_t qli_push_owner; -#endif xfs_dq_logformat_t qli_format;/* logged structure */ } xfs_dq_logitem_t;
fs/xfs/xfs_buf_item.c +35 -29
102 lines not on this page · raw message
fs/xfs/xfs_inode_item.c +15 -84
146 lines not on this page · raw message
fs/xfs/xfs_inode_item.h +0 -6
@@ -144,12 +144,6 @@ typedef struct xfs_inode_log_item {data exts */ struct xfs_bmbt_rec *ili_aextents_buf;/* array of logged attr exts */ -unsigned int ili_pushbuf_flag;/* one bit used in push_ail */ - -#ifdef DEBUG -uint64_t ili_push_owner;/* one who sets pushbuf_flag -above gets to push the buf */ -#endif #ifdef XFS_TRANS_DEBUG int ili_root_size;char *ili_orig_root;
fs/xfs/xfs_trans_ail.c +7 -0
@@ -253,6 +253,7 @@ xfsaild_push(int flush_log, count, stuck;xfs_mount_t *mp = ailp->xa_mount;struct xfs_ail_cursor *cur = &ailp->xa_cursors; +int push_xfsbufd =0 ;spin_lock (&ailp->xa_lock);xfs_trans_ail_cursor_init (ailp, cur);
@@ -308,6 +309,7 @@ xfsaild_push(XFS_STATS_INC (xs_push_ail_pushbuf);IOP_PUSHBUF (lip); last_pushed_lsn = lsn; + push_xfsbufd =1 ;break ;case XFS_ITEM_PINNED :
@@ -374,6 +376,11 @@ xfsaild_push(xfs_log_force (mp, (xfs_lsn_t )0 ,XFS_LOG_FORCE ); } +if (push_xfsbufd) { +/* we've got delayed write buffers to flush */ +xfs_flush_buftarg_delwri (mp->m_ddev_targp); + } +if (!count) {/* We're past our target or empty, so idle */ tout =0 ;
Signature · 6 lines
1.6.5 _______________________________________________ xfs mailing list [email protected] http://oss.sgi.com/mailman/listinfo/xfs
PATCH 3/3 XFS: Sort delayed write buffers before dispatch
In reply to Dave ChinnerCurrently when the xfsbufd writes delayed write buffers, it pushes them to disk in the order they come off the delayed write list. If there are lots of buffers ѕpread widely over the disk, this results in overwhelming the elevator sort queues in the block layer and we end up losing the posibility of merging adjacent buffers to minimise the number of IOs.
Add a sort array to the buftarg so that we can do high level sorting of the buffers once they are pulled off the delwri queue for writeback. Currently this array can hold 4096 buffers at a time which gives us a window 32 times larger than the default elevator maximums for ordering buffers.
Ideally this should use a list sort rather than requiring an external buffer to sort the buffers in, but for simplicity just do it via sort function. Followup patches are needed to take the list sort functions from the DRM and UBIFS code and make it a common function and to utilise it. That will allow sorting the entire delwri queue to be written in one go.
--- fs/xfs/linux-2.6/xfs_buf.c | 121 ++++++++++++++++++++++++++++++++------------ fs/xfs/linux-2.6/xfs_buf.h | 5 ++ 2 files changed, 93 insertions(+), 33 deletions(-)
fs/xfs/linux-2.6/xfs_buf.c +88 -33
@@ -1644,12 +1644,18 @@ xfs_buf_delwri_promote(spinlock_t *dwlk = &bp->b_target->bt_delwrite_lock;long age = xfs_buf_age_centisecs *msecs_to_jiffies (10 ) +1 ; -spin_lock (dwlk);ASSERT (bp->b_flags &XBF_DELWRI );ASSERT (bp->b_flags & _XBF_DELWRI_Q); -list_del (&bp->b_list); -list_add (&bp->b_list, dwq); + +/* +* Check the buffer age before locking the delayed write queue as we +* don't need to promote buffers that are already past the flush age. +*/ +if (bp->b_queuetime < jiffies - age) +return ; bp->b_queuetime = jiffies - age; +spin_lock (dwlk); +list_move (&bp->b_list, dwq);spin_unlock (dwlk); }
@@ -1723,14 +1729,55 @@ xfs_buf_delwri_split( } +/* +* Compare function is more complex than it needs to be because +* the return value is only 32 bits and we are doing comparisons +* on 64 bit values +*/ +int +xfs_buf_cmp ( +const void *a, +const void *b) +{ +const struct xfs_buf *ap = *(const struct xfs_buf **)a; +const struct xfs_buf *bp = *(const struct xfs_buf **)b; +xfs_daddr_t diff; + + diff = ap->b_bn - bp->b_bn; +if (diff <0 ) +return -1 ; +if (diff >0 ) +return 1 ; +return 0 ; +} + +int +xfs_buf_delwri_sort ( +xfs_buftarg_t *target, +struct list_head *list) +{ +int i =0 ; + +while (i <XFS_BUF_SORTBUF_SIZE && !list_empty (list)) { +struct xfs_buf *bp =list_entry (list->next, xfs_buf_t, b_list); + +ASSERT (target == bp->b_target); +list_del_init (&bp->b_list); + target->bt_sortbuf[i++] = bp; + } +sort (target->bt_sortbuf, i,sizeof (struct xfs_buf *), xfs_buf_cmp,NULL ); + + target->bt_sortbuf_num = i; +if (!list_empty (list)) +return 1 ; +return 0 ; +} + STATIC intxfsbufd (void *data) { -struct list_head tmp;xfs_buftarg_t *target = (xfs_buftarg_t *)data; -int count; -xfs_buf_t *bp; current->flags |=PF_MEMALLOC ;
@@ -1739,6 +1786,9 @@ xfsbufd( do { long age = xfs_buf_age_centisecs * msecs_to_jiffies(10); long tout = age; + int count = 0; + int more = 0; + struct list_head tmp; if (unlikely(freezing(current))) { set_bit(XBT_FORCE_SLEEP, &target->bt_flags);
@@ -1753,15 +1803,14 @@ xfsbufd(schedule_timeout_interruptible (tout);xfs_buf_delwri_split (target, &tmp, age); - count =0 ; -while (!list_empty (&tmp)) { - bp =list_entry (tmp.next, xfs_buf_t, b_list); -ASSERT (target == bp->b_target); - -list_del_init (&bp->b_list); -xfs_buf_iostrategy (bp); - count++; - } +do { +int i; + more =xfs_buf_delwri_sort (target, &tmp); +for (i =0 ; i < target->bt_sortbuf_num; i++) { +xfs_buf_iostrategy (target->bt_sortbuf[i]); + count++; + } + }while (more);if (as_list_len >0 )purge_addresses ();
@@ -1783,38 +1832,44 @@ xfs_flush_buftarg( xfs_buftarg_t *target, int wait) { -struct list_head tmp; -xfs_buf_t *bp, *n; +xfs_buf_t *bp;int pincount =0 ; +int more =0 ; +LIST_HEAD (tmp_list); +LIST_HEAD (wait_list);xfs_buf_runall_queues (xfsconvertd_workqueue);xfs_buf_runall_queues (xfsdatad_workqueue);xfs_buf_runall_queues (xfslogd_workqueue);set_bit (XBT_FORCE_FLUSH , &target->bt_flags); - pincount =xfs_buf_delwri_split (target, &tmp,0 ); + pincount =xfs_buf_delwri_split (target, &tmp_list,0 );/* -* Dropped the delayed write list lock, now walk the temporary list +* Dropped the delayed write list lock, now walk the temporary list. +* All I/O is issued async and then if we need to wait for completion +* we do that after issuing all the IO. */ -list_for_each_entry_safe (bp, n, &tmp, b_list) { -ASSERT (target == bp->b_target); -if (wait) - bp->b_flags &= ~XBF_ASYNC ; -else -list_del_init (&bp->b_list); - -xfs_buf_iostrategy (bp); - } +do { +int i; + more =xfs_buf_delwri_sort (target, &tmp_list); +for (i =0 ; i < target->bt_sortbuf_num; i++) { + bp = target->bt_sortbuf[i]; +ASSERT (target == bp->b_target); +if (wait) { + bp->b_flags &= ~XBF_ASYNC ; +list_add (&bp->b_list, &wait_list); + } +xfs_buf_iostrategy (bp); + } + }while (more);if (wait)blk_run_address_space (target->bt_mapping); -/* -* Remaining list items must be flushed before returning -*/ -while (!list_empty (&tmp)) { - bp =list_entry (tmp.next, xfs_buf_t, b_list); +/* Now wait for IO to complete if required. */ +while (!list_empty (&wait_list)) { + bp =list_entry (wait_list.next, xfs_buf_t, b_list);list_del_init (&bp->b_list);xfs_iowait (bp);
fs/xfs/linux-2.6/xfs_buf.h +5 -0
@@ -128,6 +128,8 @@ typedef struct xfs_bufhash {spinlock_t bh_lock; } xfs_bufhash_t; +#define XFS_BUF_SORTBUF_SIZE 4096 + typedef struct xfs_buftarg { dev_t bt_dev; struct block_device *bt_bdev;
@@ -147,6 +149,9 @@ typedef struct xfs_buftarg {struct list_head bt_delwrite_queue;spinlock_t bt_delwrite_lock;unsigned long bt_flags; +int bt_sortbuf_num; +struct xfs_buf * bt_sortbuf[XFS_BUF_SORTBUF_SIZE ]; + } xfs_buftarg_t;/*
Signature · 6 lines
1.6.5 _______________________________________________ xfs mailing list [email protected] http://oss.sgi.com/mailman/listinfo/xfs
Re: [PATCH 3/3] XFS: Sort delayed write buffers before dispatch
Quoted 7 lines · Dave Chinner
Currently when the xfsbufd writes delayed write buffers, it pushes them to disk in the order they come off the delayed write list. If there are lots of buffers ѕpread widely over the disk, this results in overwhelming the elevator sort queues in the block layer and we end up losing the posibility of merging adjacent buffers to minimise the number of IOs.
Quoted 5 lines
Add a sort array to the buftarg so that we can do high level sorting of the buffers once they are pulled off the delwri queue for writeback. Currently this array can hold 4096 buffers at a time which gives us a window 32 times larger than the default elevator maximums for ordering buffers.
At first look it seems a bit wasteful because the elevator sorts again. Is your window that much bigger than the elevators? Perhaps the sort queue in the elevator should be just enlarged?
Quoted 4 lines
Ideally this should use a list sort rather than requiring an external buffer to sort the buffers in, but for simplicity just do it via sort function.
Doing merge sort on lists is relatively simple There are plenty examples in a google search. An alternative is also to construct a rbtree on the fly and then walk it.
But if you use sort() this way you probably should at least add a u64 swap function to lib/sort.c, otherwise all the pointers will be exchanged byte-by-byte on 64bit systems which is rather slow.
-andi
Signature · 6 lines
[email protected] -- Speaking for myself only. _______________________________________________ xfs mailing list [email protected] http://oss.sgi.com/mailman/listinfo/xfs
Re: [PATCH 3/3] XFS: Sort delayed write buffers before dispatch
Quoted 9 lines · Andi Kleen
Quoted 7 lines · Dave Chinner
Currently when the xfsbufd writes delayed write buffers, it pushes them to disk in the order they come off the delayed write list. If there are lots of buffers ѕpread widely over the disk, this results in overwhelming the elevator sort queues in the block layer and we end up losing the posibility of merging adjacent buffers to minimise the number of IOs.
Quoted 8 lines
Quoted 5 lines
Add a sort array to the buftarg so that we can do high level sorting of the buffers once they are pulled off the delwri queue for writeback. Currently this array can hold 4096 buffers at a time which gives us a window 32 times larger than the default elevator maximums for ordering buffers.
At first look it seems a bit wasteful because the elevator sorts again. Is your window that much bigger than the elevators?
Easily - at currently limits we can log about 8000 inodes a megabyte of log space and we can write over 150MB/s to the log. That adds up to about 1.2million inodes dirtied a second, or 40,000 inode buffers a second needing to be written back.....
We have much more of a clue about what is happening at the filesytem level, and can optimise far more efficiently at higher levels. The elevator can only merge IOs if the higher layer sends it adjacent blocks.
I don't want to do buffer merging in XFS, but I want adjacent IOs merged and the elevator does that well. Rather than sending buffers down in random order and only getting a few merges, sorting all the buffers first guarantees that all possible merges are made by the elevator across the entire dispatch without needing to tweak the elevator at all.
IOWs, being smart at the higher layers where you have the context to do a good job means we don't need to add heuristics or tweaks to try to guess the best thing to do at the lower layers.
Quoted 1 line
Perhaps the sort queue in the elevator should be just enlarged?
Which we used to do and that caused all sorts of latency and OOM issues by pinning huge amounts of dirty memory in the elevators.
Quoted 7 lines
Quoted 3 lines
Ideally this should use a list sort rather than requiring an external buffer to sort the buffers in, but for simplicity just do it via sort function.
Doing merge sort on lists is relatively simple There are plenty examples in a google search. An alternative is also to construct a rbtree on the fly and then walk it.
Already got it handled - there's a couple of copies of list_sort() already in the tree - I'll post an updated patch set in the next couple of days after I've had a chance to QA it.
Cheers,
Dave.
Signature · 7 lines
Dave Chinner [email protected] _______________________________________________ xfs mailing list [email protected] http://oss.sgi.com/mailman/listinfo/xfs
Re: [PATCH 3/3] XFS: Sort delayed write buffers before dispatch
In reply to Dave ChinnerQuoted 6 lines · Dave Chinner
Ideally this should use a list sort rather than requiring an external buffer to sort the buffers in, but for simplicity just do it via sort function. Followup patches are needed to take the list sort functions from the DRM and UBIFS code and make it a common function and to utilise it. That will allow sorting the entire delwri queue to be written in one go.
Maybe getting the common list sort in ASAP might be a good idea. That way we could have it ready by the time this patchset is merged. If not adding a new list sort to XFS temporarily might be a better idea than artifically flattening the list.
_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
Re: [PATCH 3/3] XFS: Sort delayed write buffers before dispatch
Quoted 9 lines · Christoph Hellwig
Quoted 6 lines · Dave Chinner
Ideally this should use a list sort rather than requiring an external buffer to sort the buffers in, but for simplicity just do it via sort function. Followup patches are needed to take the list sort functions from the DRM and UBIFS code and make it a common function and to utilise it. That will allow sorting the entire delwri queue to be written in one go.
Quoted 4 lines
Maybe getting the common list sort in ASAP might be a good idea. That way we could have it ready by the time this patchset is merged. If not adding a new list sort to XFS temporarily might be a better idea than artifically flattening the list.
I'm about to post that and an updated patch series to use it. ;)
Cheers,
Dave.
Signature · 7 lines
Dave Chinner [email protected] _______________________________________________ xfs mailing list [email protected] http://oss.sgi.com/mailman/listinfo/xfs