xfs: fixes for 7.2-rc2

Signed-off-by: Carlos Maiolino <cem@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iJUEABMJAB0WIQSmtYVZ/MfVMGUq1GNcsMJ8RxYuYwUCakeeeQAKCRBcsMJ8RxYu
 Y6Q+AX9gnOQ1ZkxIKb+/hHq061nh/41iOaiO1CHEgvkCuX2pkRDJkIO2g5+1WaCC
 FcjSzLcBf0COW1/Pye2+ViB1u/SkMJZpKbrKl6lY4wcVHtqNSwn/D0zvbXsfBSsb
 Zj6I2kGZGw==
 =FpZC
 -----END PGP SIGNATURE-----

Merge tag 'xfs-fixes-7.2-rc2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Carlos Maiolino:
 "A collection of bugfixes and some small code refactoring"

* tag 'xfs-fixes-7.2-rc2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: simplify __xfs_buf_ioend
  xfs: fix handling of synchronous errors in xfs_buf_submit
  xfs: remove xfs_buf_ioend
  xfs: improve the xfs_buf_ioend_fail calling convention
  xfs: use null daddr for unset first bad log block
  xfs: fix memory leak in xfs_dqinode_metadir_create()
  xfs: release dquot buffer after dqflush failure
  xfs: also mark the buffer stale on verifier failure in xfs_buf_submit
  xfs: open code xfs_buf_ioend_fail in xfs_buf_submit
  xfs: fix AGFL extent count calculation in xrep_agfl_fill
  xfs: simplify the failure path in xfs_buf_alloc_vmalloc
  xfs: fix incorrect use of gfp flags in xfs_buf_alloc_backing_mem
  xfs: lift setting __GFP_NOFAIL from xfs_buf_alloc_kmem to the caller
  xfs: split up xfs_buf_alloc_backing_mem
This commit is contained in:
Linus Torvalds 2026-07-03 05:44:56 -10:00
commit 025d0d6221
8 changed files with 124 additions and 101 deletions

View File

@ -436,17 +436,27 @@ xfs_dqinode_metadir_create(
error = xfs_metadir_create(&upd, S_IFREG);
if (error)
return error;
goto out_cancel;
xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE);
error = xfs_metadir_commit(&upd);
if (error)
return error;
goto out_irele;
xfs_finish_inode_setup(upd.ip);
*ipp = upd.ip;
return 0;
out_cancel:
xfs_metadir_cancel(&upd, error);
out_irele:
/* Have to finish setting up the inode to ensure it's deleted. */
if (upd.ip) {
xfs_finish_inode_setup(upd.ip);
xfs_irele(upd.ip);
}
return error;
}
#ifndef __KERNEL__

View File

@ -652,7 +652,7 @@ xrep_agfl_fill(
while (agbno < start + len && af->fl_off < af->flcount)
af->agfl_bno[af->fl_off++] = cpu_to_be32(agbno++);
error = xagb_bitmap_set(&af->used_extents, start, agbno - 1);
error = xagb_bitmap_set(&af->used_extents, start, agbno - start);
if (error)
return error;

View File

@ -120,6 +120,22 @@ xfs_buf_free(
call_rcu(&bp->b_rcu, xfs_buf_free_callback);
}
static int
xfs_buf_alloc_folio(
struct xfs_buf *bp,
size_t size,
gfp_t gfp_mask)
{
struct folio *folio;
folio = folio_alloc(gfp_mask, get_order(size));
if (!folio)
return -ENOMEM;
bp->b_addr = folio_address(folio);
trace_xfs_buf_backing_folio(bp, _RET_IP_);
return 0;
}
static int
xfs_buf_alloc_kmem(
struct xfs_buf *bp,
@ -129,7 +145,7 @@ xfs_buf_alloc_kmem(
ASSERT(is_power_of_2(size));
ASSERT(size < PAGE_SIZE);
bp->b_addr = kmalloc(size, gfp_mask | __GFP_NOFAIL);
bp->b_addr = kmalloc(size, gfp_mask);
if (!bp->b_addr)
return -ENOMEM;
@ -148,6 +164,26 @@ xfs_buf_alloc_kmem(
return 0;
}
static int
xfs_buf_alloc_vmalloc(
struct xfs_buf *bp,
size_t size,
gfp_t gfp_mask)
{
for (;;) {
bp->b_addr = __vmalloc(size, gfp_mask);
if (bp->b_addr)
break;
if (gfp_mask & __GFP_NORETRY)
return -ENOMEM;
XFS_STATS_INC(bp->b_mount, xb_page_retries);
memalloc_retry_wait(gfp_mask);
}
trace_xfs_buf_backing_vmalloc(bp, _RET_IP_);
return 0;
}
/*
* Allocate backing memory for a buffer.
*
@ -175,7 +211,6 @@ xfs_buf_alloc_backing_mem(
{
size_t size = BBTOB(bp->b_length);
gfp_t gfp_mask = GFP_KERNEL | __GFP_NOLOCKDEP | __GFP_NOWARN;
struct folio *folio;
if (xfs_buftarg_is_mem(bp->b_target))
return xmbuf_map_backing_mem(bp);
@ -187,22 +222,6 @@ xfs_buf_alloc_backing_mem(
if (flags & XBF_READ_AHEAD)
gfp_mask |= __GFP_NORETRY;
/*
* For buffers smaller than PAGE_SIZE use a kmalloc allocation if that
* is properly aligned. The slab allocator now guarantees an aligned
* allocation for all power of two sizes, which matches most of the
* smaller than PAGE_SIZE buffers used by XFS.
*/
if (size < PAGE_SIZE && is_power_of_2(size))
return xfs_buf_alloc_kmem(bp, size, gfp_mask);
/*
* Don't bother with the retry loop for single PAGE allocations: vmalloc
* won't do any better.
*/
if (size <= PAGE_SIZE)
gfp_mask |= __GFP_NOFAIL;
/*
* Optimistically attempt a single high order folio allocation for
* larger than PAGE_SIZE buffers.
@ -215,35 +234,31 @@ xfs_buf_alloc_backing_mem(
* path for them instead of wasting memory here.
*/
if (size > PAGE_SIZE) {
if (!is_power_of_2(size))
goto fallback;
gfp_mask &= ~__GFP_DIRECT_RECLAIM;
gfp_mask |= __GFP_NORETRY;
}
folio = folio_alloc(gfp_mask, get_order(size));
if (!folio) {
if (size <= PAGE_SIZE)
return -ENOMEM;
trace_xfs_buf_backing_fallback(bp, _RET_IP_);
goto fallback;
}
bp->b_addr = folio_address(folio);
trace_xfs_buf_backing_folio(bp, _RET_IP_);
return 0;
if (is_power_of_2(size)) {
gfp_t folio_gfp = gfp_mask;
fallback:
for (;;) {
bp->b_addr = __vmalloc(size, gfp_mask);
if (bp->b_addr)
break;
if (flags & XBF_READ_AHEAD)
return -ENOMEM;
XFS_STATS_INC(bp->b_mount, xb_page_retries);
memalloc_retry_wait(gfp_mask);
folio_gfp &= ~__GFP_DIRECT_RECLAIM;
folio_gfp |= __GFP_NORETRY;
if (xfs_buf_alloc_folio(bp, size, folio_gfp) == 0)
return 0;
trace_xfs_buf_backing_fallback(bp, _RET_IP_);
}
return xfs_buf_alloc_vmalloc(bp, size, gfp_mask);
}
trace_xfs_buf_backing_vmalloc(bp, _RET_IP_);
return 0;
/*
* The slab allocator now guarantees aligned allocations for all power
* of two sizes. This covers most smaller XFS buffers, so just use
* kmalloc in this case.
*
* Don't bother with the vmalloc fallback for allocations of page size
* or less: vmalloc won't do any better.
*/
if (!(gfp_mask & __GFP_NORETRY))
gfp_mask |= __GFP_NOFAIL;
if (size < PAGE_SIZE && is_power_of_2(size))
return xfs_buf_alloc_kmem(bp, size, gfp_mask);
return xfs_buf_alloc_folio(bp, size, gfp_mask);
}
static int
@ -1083,11 +1098,17 @@ xfs_buf_ioend_handle_error(
return false;
}
/* returns false if the caller needs to resubmit the I/O, else true */
static bool
__xfs_buf_ioend(
/*
* Complete a buffer read or write.
*
* Releases the buffer if the I/O was asynchronous.
*/
static void
xfs_buf_ioend(
struct xfs_buf *bp)
{
bool async = bp->b_flags & XBF_ASYNC;
trace_xfs_buf_iodone(bp, _RET_IP_);
if (bp->b_flags & XBF_READ) {
@ -1101,14 +1122,16 @@ __xfs_buf_ioend(
if (bp->b_flags & XBF_READ_AHEAD)
percpu_counter_dec(&bp->b_target->bt_readahead_count);
} else {
if (!bp->b_error) {
if (unlikely(bp->b_error)) {
if (xfs_buf_ioend_handle_error(bp)) {
ASSERT(async);
return;
}
} else {
bp->b_flags &= ~XBF_WRITE_FAIL;
bp->b_flags |= XBF_DONE;
}
if (unlikely(bp->b_error) && xfs_buf_ioend_handle_error(bp))
return false;
/* clear the retry state */
bp->b_last_error = 0;
bp->b_retries = 0;
@ -1128,30 +1151,15 @@ __xfs_buf_ioend(
bp->b_flags &= ~(XBF_READ | XBF_WRITE | XBF_READ_AHEAD |
_XBF_LOGRECOVERY);
return true;
}
static void
xfs_buf_ioend(
struct xfs_buf *bp)
{
if (!__xfs_buf_ioend(bp))
return;
if (bp->b_flags & XBF_ASYNC)
if (async)
xfs_buf_relse(bp);
else
complete(&bp->b_iowait);
}
static void
xfs_buf_ioend_work(
struct work_struct *work)
{
struct xfs_buf *bp =
container_of(work, struct xfs_buf, b_ioend_work);
if (__xfs_buf_ioend(bp))
xfs_buf_relse(bp);
xfs_buf_ioend(container_of(work, struct xfs_buf, b_ioend_work));
}
void
@ -1177,17 +1185,18 @@ xfs_buf_ioerror_alert(
}
/*
* To simulate an I/O failure, the buffer must be locked and held with at least
* two references.
* Fail a locked and referenced buffer outside the I/O path.
*
* The buf item reference is dropped via ioend processing. The second reference
* is owned by the caller and is dropped on I/O completion if the buffer is
* XBF_ASYNC.
* The caller transfers a reference which will be released after processing the
* error.
*/
void
xfs_buf_ioend_fail(
xfs_buf_fail(
struct xfs_buf *bp)
{
ASSERT(xfs_buf_islocked(bp));
bp->b_flags |= XBF_ASYNC;
bp->b_flags &= ~XBF_DONE;
xfs_buf_stale(bp);
xfs_buf_ioerror(bp, -EIO);
@ -1300,12 +1309,11 @@ xfs_buf_iowait(
{
ASSERT(!(bp->b_flags & XBF_ASYNC));
do {
trace_xfs_buf_iowait(bp, _RET_IP_);
wait_for_completion(&bp->b_iowait);
trace_xfs_buf_iowait_done(bp, _RET_IP_);
} while (!__xfs_buf_ioend(bp));
trace_xfs_buf_iowait(bp, _RET_IP_);
wait_for_completion(&bp->b_iowait);
trace_xfs_buf_iowait_done(bp, _RET_IP_);
xfs_buf_ioend(bp);
return bp->b_error;
}
@ -1369,8 +1377,8 @@ xfs_buf_submit(
* on shutdown.
*/
if (bp->b_mount->m_log && xlog_is_shutdown(bp->b_mount->m_log)) {
xfs_buf_ioend_fail(bp);
return;
xfs_buf_ioerror(bp, -EIO);
goto ioerror;
}
if (bp->b_flags & XBF_WRITE)
@ -1383,18 +1391,26 @@ xfs_buf_submit(
bp->b_error = 0;
if ((bp->b_flags & XBF_WRITE) && !xfs_buf_verify_write(bp)) {
/* ->verify_write should have set b_error already */
xfs_force_shutdown(bp->b_mount, SHUTDOWN_CORRUPT_INCORE);
xfs_buf_ioend(bp);
return;
goto ioerror;
}
/* In-memory targets are directly mapped, no I/O required. */
if (xfs_buftarg_is_mem(bp->b_target)) {
xfs_buf_ioend(bp);
return;
}
if (xfs_buftarg_is_mem(bp->b_target))
goto end_io;
xfs_buf_submit_bio(bp);
return;
ioerror:
bp->b_flags &= ~XBF_DONE;
xfs_buf_stale(bp);
end_io:
if (bp->b_flags & XBF_ASYNC)
xfs_buf_ioend(bp);
else
complete(&bp->b_iowait);
}
/*

View File

@ -290,7 +290,7 @@ extern void __xfs_buf_ioerror(struct xfs_buf *bp, int error,
xfs_failaddr_t failaddr);
#define xfs_buf_ioerror(bp, err) __xfs_buf_ioerror((bp), (err), __this_address)
extern void xfs_buf_ioerror_alert(struct xfs_buf *bp, xfs_failaddr_t fa);
void xfs_buf_ioend_fail(struct xfs_buf *);
void xfs_buf_fail(struct xfs_buf *bp);
void __xfs_buf_mark_corrupt(struct xfs_buf *bp, xfs_failaddr_t fa);
#define xfs_buf_mark_corrupt(bp) __xfs_buf_mark_corrupt((bp), __this_address)

View File

@ -549,8 +549,7 @@ xfs_buf_item_unpin(
* wait for the lock and then run the IO failure completion.
*/
xfs_buf_lock(bp);
bp->b_flags |= XBF_ASYNC;
xfs_buf_ioend_fail(bp);
xfs_buf_fail(bp);
return;
}

View File

@ -2646,8 +2646,7 @@ xfs_iflush_cluster(
* inode cluster buffers.
*/
xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
bp->b_flags |= XBF_ASYNC;
xfs_buf_ioend_fail(bp);
xfs_buf_fail(bp);
return error;
}

View File

@ -1028,7 +1028,7 @@ xlog_verify_head(
{
struct xlog_rec_header *tmp_rhead;
char *tmp_buffer;
xfs_daddr_t first_bad;
xfs_daddr_t first_bad = XFS_BUF_DADDR_NULL;
xfs_daddr_t tmp_rhead_blk;
int found;
int error;
@ -1057,7 +1057,8 @@ xlog_verify_head(
*/
error = xlog_do_recovery_pass(log, *head_blk, tmp_rhead_blk,
XLOG_RECOVER_CRCPASS, &first_bad);
if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) {
if ((error == -EFSBADCRC || error == -EFSCORRUPTED) &&
first_bad != XFS_BUF_DADDR_NULL) {
/*
* We've hit a potential torn write. Reset the error and warn
* about it.
@ -3575,4 +3576,3 @@ xlog_recover_cancel(
if (xlog_recovery_needed(log))
xlog_recover_cancel_intents(log);
}

View File

@ -166,10 +166,9 @@ xfs_qm_dqpurge(
* does it on success.
*/
error = xfs_qm_dqflush(dqp, bp);
if (!error) {
if (!error)
error = xfs_bwrite(bp);
xfs_buf_relse(bp);
}
xfs_buf_relse(bp);
xfs_dqflock(dqp);
}
xfs_dquot_detach_buf(dqp);