summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-07-03 05:44:56 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-07-03 05:44:56 -1000
commit025d0d6221d9b060bce251427c671cd0080d9dae (patch)
tree73437f1b164beaca429ebe1dbe510dd34064f65e
parent4dbc94bcc2df0c3bba40318c0751a8f487486783 (diff)
parente4281086ae6caf006b6ef0670479eb5f96880fb9 (diff)
downloadlinux-stable-025d0d6221d9b060bce251427c671cd0080d9dae.tar.gz
linux-stable-025d0d6221d9b060bce251427c671cd0080d9dae.zip
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
-rw-r--r--fs/xfs/libxfs/xfs_dquot_buf.c14
-rw-r--r--fs/xfs/scrub/agheader_repair.c2
-rw-r--r--fs/xfs/xfs_buf.c192
-rw-r--r--fs/xfs/xfs_buf.h2
-rw-r--r--fs/xfs/xfs_buf_item.c3
-rw-r--r--fs/xfs/xfs_inode.c3
-rw-r--r--fs/xfs/xfs_log_recover.c6
-rw-r--r--fs/xfs/xfs_qm.c5
8 files changed, 125 insertions, 102 deletions
diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c
index ce767b40482f..bbada0d3cc08 100644
--- a/fs/xfs/libxfs/xfs_dquot_buf.c
+++ b/fs/xfs/libxfs/xfs_dquot_buf.c
@@ -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__
diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c
index 7114c9a44182..2554494847ff 100644
--- a/fs/xfs/scrub/agheader_repair.c
+++ b/fs/xfs/scrub/agheader_repair.c
@@ -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;
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index 3ce12fe1c307..e1465e950acc 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -121,6 +121,22 @@ xfs_buf_free(
}
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,
size_t size,
@@ -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);
@@ -188,22 +223,6 @@ xfs_buf_alloc_backing_mem(
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;
-
-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);
+ if (is_power_of_2(size)) {
+ gfp_t folio_gfp = 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 @@ out_stale:
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);
}
/*
diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h
index b3cd1c7029f1..79cc9c3f0254 100644
--- a/fs/xfs/xfs_buf.h
+++ b/fs/xfs/xfs_buf.h
@@ -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)
diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c
index 8487635579e5..1f055cd6732e 100644
--- a/fs/xfs/xfs_buf_item.c
+++ b/fs/xfs/xfs_buf_item.c
@@ -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;
}
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 317eb57f989f..15279d22a894 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -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;
}
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 09e6678ca487..5f984bf5698a 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -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);
}
-
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index aa0d2976f1c3..896b24f87ac9 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -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);