Thread (7 messages) 7 messages, 4 authors, 2018-08-24

Re: [PATCH] xfs: don't unlock invalidated buf on aborted tx commit

From: Christoph Hellwig <hch@infradead.org>
Date: 2018-08-22 09:24:22

+	int			freed;
I think this should be a bool..
 	bool			aborted;
 	bool			hold = !!(bip->bli_flags & XFS_BLI_HOLD);
 	bool			dirty = !!(bip->bli_flags & XFS_BLI_DIRTY);
+	bool			stale = !!(bip->bli_flags & XFS_BLI_STALE);
 #if defined(DEBUG) || defined(XFS_WARN)
 	bool			ordered = !!(bip->bli_flags & XFS_BLI_ORDERED);
 #endif
+	/*
+	 * The bli dirty state should match whether the blf has logged segments
+	 * except for ordered buffers, where only the bli should be dirty.
+	 */
+	ASSERT((!ordered && dirty == xfs_buf_item_dirty_format(bip)) ||
+	       (ordered && dirty && !xfs_buf_item_dirty_format(bip)));
+	ASSERT(!stale || (bip->__bli_format.blf_flags & XFS_BLF_CANCEL));
 
+	trace_xfs_buf_item_unlock(bip);
 
+	aborted = test_bit(XFS_LI_ABORTED, &lip->li_flags);
 
 	/*
+	 * Clear the buffer's association with this transaction and
+	 * per-transaction state from the bli, which has been copied above.
 	 */
+	bp->b_transp = NULL;
 	bip->bli_flags &= ~(XFS_BLI_LOGGED | XFS_BLI_HOLD | XFS_BLI_ORDERED);
 
 	/*
+	 * Drop the transaction bli reference and free the item if clean or
+	 * aborted and we had the last reference. In either case this is the
+	 * last opportunity to free the item since it won't be written back.
+	 * Otherwise, the bli is still referenced or dirty and will be freed on
+	 * final unpin of the buffer (if stale) or writeback completion.
 	 */
+	freed = atomic_dec_and_test(&bip->bli_refcount);
+	if (freed && (aborted || !dirty)) {
+		ASSERT(!aborted || XFS_FORCED_SHUTDOWN(lip->li_mountp));
+		ASSERT(!stale || aborted);
+		/* an aborted item may be in the AIL, remove it first */
+		if (aborted)
+			xfs_trans_ail_remove(lip, SHUTDOWN_LOG_IO_ERROR);
+		xfs_buf_item_relse(bp);
 	}
 
 	/*
+	 * If the buffer was invalidated, leave it locked on transaction commit
+	 * unless we just dropped the final reference. The latter case should
+	 * only ever happen on abort because invalidation dirties the
+	 * transaction and the log would have grabbed another bli reference when
+	 * the buffer was pinned. In the non-abort case, the buffer is unlocked
+	 * on final unpin and the bli freed since stale buffers are not written
+	 * back.
 	 */
+	if (stale && !freed)
+		return;
+	ASSERT(!stale || (aborted && freed));
 
 	if (!hold)
 		xfs_buf_relse(bp);
I find the logic much more convoluted than what was there before.

I seems like we could apply your stale fix without reshuffling the
code like this:

	if (atomic_dec_and_test(&bip->bli_refcount)) {
		if (aborted) {
			ASSERT(XFS_FORCED_SHUTDOWN(lip->li_mountp));
			xfs_trans_ail_remove(lip, SHUTDOWN_LOG_IO_ERROR);
			xfs_buf_item_relse(bp);
		} else if (!dirty) {
			ASSERT(!stale);
			xfs_buf_item_relse(bp);
		}
	} else {
		if (stale)
			return;
	}
	
	if (!hold)
		xfs_buf_relse(bp);

which at least to me is a lot easier to read.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help