// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. * Copyright (c) 2008 Dave Chinner * All Rights Reserved. */ #include "xfs.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" #include "xfs_trans_resv.h" #include "xfs_mount.h" #include "xfs_trans.h" #include "xfs_trans_priv.h" #include "xfs_trace.h" #include "xfs_errortag.h" #include "xfs_error.h" #include "xfs_log.h" #include "xfs_log_priv.h" #ifdef DEBUG /* * Check that the list is sorted as it should be. * * Called with the ail lock held, but we don't want to assert fail with it * held otherwise we'll lock everything up and won't be able to debug the * cause. Hence we sample and check the state under the AIL lock and return if * everything is fine, otherwise we drop the lock and run the ASSERT checks. * Asserts may not be fatal, so pick the lock back up and continue onwards. */ STATIC void xfs_ail_check( struct xfs_ail *ailp, struct xfs_log_item *lip) __must_hold(&ailp->ail_lock) { … } #else /* !DEBUG */ #define xfs_ail_check … #endif /* DEBUG */ /* * Return a pointer to the last item in the AIL. If the AIL is empty, then * return NULL. */ static struct xfs_log_item * xfs_ail_max( struct xfs_ail *ailp) { … } /* * Return a pointer to the item which follows the given item in the AIL. If * the given item is the last item in the list, then return NULL. */ static struct xfs_log_item * xfs_ail_next( struct xfs_ail *ailp, struct xfs_log_item *lip) { … } /* * This is called by the log manager code to determine the LSN of the tail of * the log. This is exactly the LSN of the first item in the AIL. If the AIL * is empty, then this function returns 0. * * We need the AIL lock in order to get a coherent read of the lsn of the last * item in the AIL. */ static xfs_lsn_t __xfs_ail_min_lsn( struct xfs_ail *ailp) { … } xfs_lsn_t xfs_ail_min_lsn( struct xfs_ail *ailp) { … } /* * The cursor keeps track of where our current traversal is up to by tracking * the next item in the list for us. However, for this to be safe, removing an * object from the AIL needs to invalidate any cursor that points to it. hence * the traversal cursor needs to be linked to the struct xfs_ail so that * deletion can search all the active cursors for invalidation. */ STATIC void xfs_trans_ail_cursor_init( struct xfs_ail *ailp, struct xfs_ail_cursor *cur) { … } /* * Get the next item in the traversal and advance the cursor. If the cursor * was invalidated (indicated by a lip of 1), restart the traversal. */ struct xfs_log_item * xfs_trans_ail_cursor_next( struct xfs_ail *ailp, struct xfs_ail_cursor *cur) { … } /* * When the traversal is complete, we need to remove the cursor from the list * of traversing cursors. */ void xfs_trans_ail_cursor_done( struct xfs_ail_cursor *cur) { … } /* * Invalidate any cursor that is pointing to this item. This is called when an * item is removed from the AIL. Any cursor pointing to this object is now * invalid and the traversal needs to be terminated so it doesn't reference a * freed object. We set the low bit of the cursor item pointer so we can * distinguish between an invalidation and the end of the list when getting the * next item from the cursor. */ STATIC void xfs_trans_ail_cursor_clear( struct xfs_ail *ailp, struct xfs_log_item *lip) { … } /* * Find the first item in the AIL with the given @lsn by searching in ascending * LSN order and initialise the cursor to point to the next item for a * ascending traversal. Pass a @lsn of zero to initialise the cursor to the * first item in the AIL. Returns NULL if the list is empty. */ struct xfs_log_item * xfs_trans_ail_cursor_first( struct xfs_ail *ailp, struct xfs_ail_cursor *cur, xfs_lsn_t lsn) { … } static struct xfs_log_item * __xfs_trans_ail_cursor_last( struct xfs_ail *ailp, xfs_lsn_t lsn) { … } /* * Find the last item in the AIL with the given @lsn by searching in descending * LSN order and initialise the cursor to point to that item. If there is no * item with the value of @lsn, then it sets the cursor to the last item with an * LSN lower than @lsn. Returns NULL if the list is empty. */ struct xfs_log_item * xfs_trans_ail_cursor_last( struct xfs_ail *ailp, struct xfs_ail_cursor *cur, xfs_lsn_t lsn) { … } /* * Splice the log item list into the AIL at the given LSN. We splice to the * tail of the given LSN to maintain insert order for push traversals. The * cursor is optional, allowing repeated updates to the same LSN to avoid * repeated traversals. This should not be called with an empty list. */ static void xfs_ail_splice( struct xfs_ail *ailp, struct xfs_ail_cursor *cur, struct list_head *list, xfs_lsn_t lsn) { … } /* * Delete the given item from the AIL. Return a pointer to the item. */ static void xfs_ail_delete( struct xfs_ail *ailp, struct xfs_log_item *lip) { … } /* * Requeue a failed buffer for writeback. * * We clear the log item failed state here as well, but we have to be careful * about reference counts because the only active reference counts on the buffer * may be the failed log items. Hence if we clear the log item failed state * before queuing the buffer for IO we can release all active references to * the buffer and free it, leading to use after free problems in * xfs_buf_delwri_queue. It makes no difference to the buffer or log items which * order we process them in - the buffer is locked, and we own the buffer list * so nothing on them is going to change while we are performing this action. * * Hence we can safely queue the buffer for IO before we clear the failed log * item state, therefore always having an active reference to the buffer and * avoiding the transient zero-reference state that leads to use-after-free. */ static inline int xfsaild_resubmit_item( struct xfs_log_item *lip, struct list_head *buffer_list) { … } static inline uint xfsaild_push_item( struct xfs_ail *ailp, struct xfs_log_item *lip) { … } /* * Compute the LSN that we'd need to push the log tail towards in order to have * at least 25% of the log space free. If the log free space already meets this * threshold, this function returns the lowest LSN in the AIL to slowly keep * writeback ticking over and the tail of the log moving forward. */ static xfs_lsn_t xfs_ail_calc_push_target( struct xfs_ail *ailp) { … } static long xfsaild_push( struct xfs_ail *ailp) { … } static int xfsaild( void *data) { … } /* * Push out all items in the AIL immediately and wait until the AIL is empty. */ void xfs_ail_push_all_sync( struct xfs_ail *ailp) { … } void __xfs_ail_assign_tail_lsn( struct xfs_ail *ailp) { … } /* * Callers should pass the original tail lsn so that we can detect if the tail * has moved as a result of the operation that was performed. If the caller * needs to force a tail space update, it should pass NULLCOMMITLSN to bypass * the "did the tail LSN change?" checks. If the caller wants to avoid a tail * update (e.g. it knows the tail did not change) it should pass an @old_lsn of * 0. */ void xfs_ail_update_finish( struct xfs_ail *ailp, xfs_lsn_t old_lsn) __releases(ailp->ail_lock) { … } /* * xfs_trans_ail_update - bulk AIL insertion operation. * * @xfs_trans_ail_update takes an array of log items that all need to be * positioned at the same LSN in the AIL. If an item is not in the AIL, it will * be added. Otherwise, it will be repositioned by removing it and re-adding * it to the AIL. If we move the first item in the AIL, update the log tail to * match the new minimum LSN in the AIL. * * This function takes the AIL lock once to execute the update operations on * all the items in the array, and as such should not be called with the AIL * lock held. As a result, once we have the AIL lock, we need to check each log * item LSN to confirm it needs to be moved forward in the AIL. * * To optimise the insert operation, we delete all the items from the AIL in * the first pass, moving them into a temporary list, then splice the temporary * list into the correct position in the AIL. This avoids needing to do an * insert operation on every item. * * This function must be called with the AIL lock held. The lock is dropped * before returning. */ void xfs_trans_ail_update_bulk( struct xfs_ail *ailp, struct xfs_ail_cursor *cur, struct xfs_log_item **log_items, int nr_items, xfs_lsn_t lsn) __releases(ailp->ail_lock) { … } /* Insert a log item into the AIL. */ void xfs_trans_ail_insert( struct xfs_ail *ailp, struct xfs_log_item *lip, xfs_lsn_t lsn) { … } /* * Delete one log item from the AIL. * * If this item was at the tail of the AIL, return the LSN of the log item so * that we can use it to check if the LSN of the tail of the log has moved * when finishing up the AIL delete process in xfs_ail_update_finish(). */ xfs_lsn_t xfs_ail_delete_one( struct xfs_ail *ailp, struct xfs_log_item *lip) { … } void xfs_trans_ail_delete( struct xfs_log_item *lip, int shutdown_type) { … } int xfs_trans_ail_init( xfs_mount_t *mp) { … } void xfs_trans_ail_destroy( xfs_mount_t *mp) { … }