ed3b4d6cdc
When we free a metadata extent, we record it in the per-AG busy extent array so that it is not re-used before the freeing transaction hits the disk. This array is fixed size, so when it overflows we make further allocation transactions synchronous because we cannot track more freed extents until those transactions hit the disk and are completed. Under heavy mixed allocation and freeing workloads with large log buffers, we can overflow this array quite easily. Further, the array is sparsely populated, which means that inserts need to search for a free slot, and array searches often have to search many more slots that are actually used to check all the busy extents. Quite inefficient, really. To enable this aspect of extent freeing to scale better, we need a structure that can grow dynamically. While in other areas of XFS we have used radix trees, the extents being freed are at random locations on disk so are better suited to being indexed by an rbtree. So, use a per-AG rbtree indexed by block number to track busy extents. This incures a memory allocation when marking an extent busy, but should not occur too often in low memory situations. This should scale to an arbitrary number of extents so should not be a limitation for features such as in-memory aggregation of transactions. However, there are still situations where we can't avoid allocating busy extents (such as allocation from the AGFL). To minimise the overhead of such occurences, we need to avoid doing a synchronous log force while holding the AGF locked to ensure that the previous transactions are safely on disk before we use the extent. We can do this by marking the transaction doing the allocation as synchronous rather issuing a log force. Because of the locking involved and the ordering of transactions, the synchronous transaction provides the same guarantees as a synchronous log force because it ensures that all the prior transactions are already on disk when the synchronous transaction hits the disk. i.e. it preserves the free->allocate order of the extent correctly in recovery. By doing this, we avoid holding the AGF locked while log writes are in progress, hence reducing the length of time the lock is held and therefore we increase the rate at which we can allocate and free from the allocation group, thereby increasing overall throughput. The only problem with this approach is that when a metadata buffer is marked stale (e.g. a directory block is removed), then buffer remains pinned and locked until the log goes to disk. The issue here is that if that stale buffer is reallocated in a subsequent transaction, the attempt to lock that buffer in the transaction will hang waiting the log to go to disk to unlock and unpin the buffer. Hence if someone tries to lock a pinned, stale, locked buffer we need to push on the log to get it unlocked ASAP. Effectively we are trading off a guaranteed log force for a much less common trigger for log force to occur. Ideally we should not reallocate busy extents. That is a much more complex fix to the problem as it involves direct intervention in the allocation btree searches in many places. This is left to a future set of modifications. Finally, now that we track busy extents in allocated memory, we don't need the descriptors in the transaction structure to point to them. We can replace the complex busy chunk infrastructure with a simple linked list of busy extents. This allows us to remove a large chunk of code, making the overall change a net reduction in code size. Signed-off-by: Dave Chinner <david@fromorbit.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Alex Elder <aelder@sgi.com>
133 lines
4.1 KiB
C
133 lines
4.1 KiB
C
/*
|
|
* Copyright (c) 2000,2002,2005 Silicon Graphics, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it would be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
#ifndef __XFS_TRANS_PRIV_H__
|
|
#define __XFS_TRANS_PRIV_H__
|
|
|
|
struct xfs_log_item;
|
|
struct xfs_log_item_desc;
|
|
struct xfs_mount;
|
|
struct xfs_trans;
|
|
|
|
/*
|
|
* From xfs_trans_item.c
|
|
*/
|
|
struct xfs_log_item_desc *xfs_trans_add_item(struct xfs_trans *,
|
|
struct xfs_log_item *);
|
|
void xfs_trans_free_item(struct xfs_trans *,
|
|
struct xfs_log_item_desc *);
|
|
struct xfs_log_item_desc *xfs_trans_find_item(struct xfs_trans *,
|
|
struct xfs_log_item *);
|
|
struct xfs_log_item_desc *xfs_trans_first_item(struct xfs_trans *);
|
|
struct xfs_log_item_desc *xfs_trans_next_item(struct xfs_trans *,
|
|
struct xfs_log_item_desc *);
|
|
void xfs_trans_free_items(struct xfs_trans *, int);
|
|
void xfs_trans_unlock_items(struct xfs_trans *,
|
|
xfs_lsn_t);
|
|
|
|
/*
|
|
* AIL traversal cursor.
|
|
*
|
|
* Rather than using a generation number for detecting changes in the ail, use
|
|
* a cursor that is protected by the ail lock. The aild cursor exists in the
|
|
* struct xfs_ail, but other traversals can declare it on the stack and link it
|
|
* to the ail list.
|
|
*
|
|
* When an object is deleted from or moved int the AIL, the cursor list is
|
|
* searched to see if the object is a designated cursor item. If it is, it is
|
|
* deleted from the cursor so that the next time the cursor is used traversal
|
|
* will return to the start.
|
|
*
|
|
* This means a traversal colliding with a removal will cause a restart of the
|
|
* list scan, rather than any insertion or deletion anywhere in the list. The
|
|
* low bit of the item pointer is set if the cursor has been invalidated so
|
|
* that we can tell the difference between invalidation and reaching the end
|
|
* of the list to trigger traversal restarts.
|
|
*/
|
|
struct xfs_ail_cursor {
|
|
struct xfs_ail_cursor *next;
|
|
struct xfs_log_item *item;
|
|
};
|
|
|
|
/*
|
|
* Private AIL structures.
|
|
*
|
|
* Eventually we need to drive the locking in here as well.
|
|
*/
|
|
struct xfs_ail {
|
|
struct xfs_mount *xa_mount;
|
|
struct list_head xa_ail;
|
|
uint xa_gen;
|
|
struct task_struct *xa_task;
|
|
xfs_lsn_t xa_target;
|
|
struct xfs_ail_cursor xa_cursors;
|
|
spinlock_t xa_lock;
|
|
};
|
|
|
|
/*
|
|
* From xfs_trans_ail.c
|
|
*/
|
|
void xfs_trans_ail_update(struct xfs_ail *ailp,
|
|
struct xfs_log_item *lip, xfs_lsn_t lsn)
|
|
__releases(ailp->xa_lock);
|
|
void xfs_trans_ail_delete(struct xfs_ail *ailp,
|
|
struct xfs_log_item *lip)
|
|
__releases(ailp->xa_lock);
|
|
void xfs_trans_ail_push(struct xfs_ail *, xfs_lsn_t);
|
|
void xfs_trans_unlocked_item(struct xfs_ail *,
|
|
xfs_log_item_t *);
|
|
|
|
xfs_lsn_t xfs_trans_ail_tail(struct xfs_ail *ailp);
|
|
|
|
struct xfs_log_item *xfs_trans_ail_cursor_first(struct xfs_ail *ailp,
|
|
struct xfs_ail_cursor *cur,
|
|
xfs_lsn_t lsn);
|
|
struct xfs_log_item *xfs_trans_ail_cursor_next(struct xfs_ail *ailp,
|
|
struct xfs_ail_cursor *cur);
|
|
void xfs_trans_ail_cursor_done(struct xfs_ail *ailp,
|
|
struct xfs_ail_cursor *cur);
|
|
|
|
long xfsaild_push(struct xfs_ail *, xfs_lsn_t *);
|
|
void xfsaild_wakeup(struct xfs_ail *, xfs_lsn_t);
|
|
int xfsaild_start(struct xfs_ail *);
|
|
void xfsaild_stop(struct xfs_ail *);
|
|
|
|
#if BITS_PER_LONG != 64
|
|
static inline void
|
|
xfs_trans_ail_copy_lsn(
|
|
struct xfs_ail *ailp,
|
|
xfs_lsn_t *dst,
|
|
xfs_lsn_t *src)
|
|
{
|
|
ASSERT(sizeof(xfs_lsn_t) == 8); /* don't lock if it shrinks */
|
|
spin_lock(&ailp->xa_lock);
|
|
*dst = *src;
|
|
spin_unlock(&ailp->xa_lock);
|
|
}
|
|
#else
|
|
static inline void
|
|
xfs_trans_ail_copy_lsn(
|
|
struct xfs_ail *ailp,
|
|
xfs_lsn_t *dst,
|
|
xfs_lsn_t *src)
|
|
{
|
|
ASSERT(sizeof(xfs_lsn_t) == 8);
|
|
*dst = *src;
|
|
}
|
|
#endif
|
|
#endif /* __XFS_TRANS_PRIV_H__ */
|