Skip to content

Commit d3a304b

Browse files
cmaiolinodjwong
authored andcommitted
xfs: Properly retry failed inode items in case of error during buffer writeback
When a buffer has been failed during writeback, the inode items into it are kept flush locked, and are never resubmitted due the flush lock, so, if any buffer fails to be written, the items in AIL are never written to disk and never unlocked. This causes unmount operation to hang due these items flush locked in AIL, but this also causes the items in AIL to never be written back, even when the IO device comes back to normal. I've been testing this patch with a DM-thin device, creating a filesystem larger than the real device. When writing enough data to fill the DM-thin device, XFS receives ENOSPC errors from the device, and keep spinning on xfsaild (when 'retry forever' configuration is set). At this point, the filesystem can not be unmounted because of the flush locked items in AIL, but worse, the items in AIL are never retried at all (once xfs_inode_item_push() will skip the items that are flush locked), even if the underlying DM-thin device is expanded to the proper size. This patch fixes both cases, retrying any item that has been failed previously, using the infra-structure provided by the previous patch. Reviewed-by: Brian Foster <bfoster@redhat.com> Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
1 parent 0b80ae6 commit d3a304b

File tree

6 files changed

+108
-5
lines changed

6 files changed

+108
-5
lines changed

fs/xfs/xfs_buf_item.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,3 +1234,31 @@ xfs_buf_iodone(
12341234
xfs_trans_ail_delete(ailp, lip, SHUTDOWN_CORRUPT_INCORE);
12351235
xfs_buf_item_free(BUF_ITEM(lip));
12361236
}
1237+
1238+
/*
1239+
* Requeue a failed buffer for writeback
1240+
*
1241+
* Return true if the buffer has been re-queued properly, false otherwise
1242+
*/
1243+
bool
1244+
xfs_buf_resubmit_failed_buffers(
1245+
struct xfs_buf *bp,
1246+
struct xfs_log_item *lip,
1247+
struct list_head *buffer_list)
1248+
{
1249+
struct xfs_log_item *next;
1250+
1251+
/*
1252+
* Clear XFS_LI_FAILED flag from all items before resubmit
1253+
*
1254+
* XFS_LI_FAILED set/clear is protected by xa_lock, caller this
1255+
* function already have it acquired
1256+
*/
1257+
for (; lip; lip = next) {
1258+
next = lip->li_bio_list;
1259+
xfs_clear_li_failed(lip);
1260+
}
1261+
1262+
/* Add this buffer back to the delayed write list */
1263+
return xfs_buf_delwri_queue(bp, buffer_list);
1264+
}

fs/xfs/xfs_buf_item.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ void xfs_buf_attach_iodone(struct xfs_buf *,
7070
xfs_log_item_t *);
7171
void xfs_buf_iodone_callbacks(struct xfs_buf *);
7272
void xfs_buf_iodone(struct xfs_buf *, struct xfs_log_item *);
73+
bool xfs_buf_resubmit_failed_buffers(struct xfs_buf *,
74+
struct xfs_log_item *,
75+
struct list_head *);
7376

7477
extern kmem_zone_t *xfs_buf_item_zone;
7578

fs/xfs/xfs_inode_item.c

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "xfs_error.h"
2828
#include "xfs_trace.h"
2929
#include "xfs_trans_priv.h"
30+
#include "xfs_buf_item.h"
3031
#include "xfs_log.h"
3132

3233

@@ -475,6 +476,23 @@ xfs_inode_item_unpin(
475476
wake_up_bit(&ip->i_flags, __XFS_IPINNED_BIT);
476477
}
477478

479+
/*
480+
* Callback used to mark a buffer with XFS_LI_FAILED when items in the buffer
481+
* have been failed during writeback
482+
*
483+
* This informs the AIL that the inode is already flush locked on the next push,
484+
* and acquires a hold on the buffer to ensure that it isn't reclaimed before
485+
* dirty data makes it to disk.
486+
*/
487+
STATIC void
488+
xfs_inode_item_error(
489+
struct xfs_log_item *lip,
490+
struct xfs_buf *bp)
491+
{
492+
ASSERT(xfs_isiflocked(INODE_ITEM(lip)->ili_inode));
493+
xfs_set_li_failed(lip, bp);
494+
}
495+
478496
STATIC uint
479497
xfs_inode_item_push(
480498
struct xfs_log_item *lip,
@@ -484,13 +502,28 @@ xfs_inode_item_push(
484502
{
485503
struct xfs_inode_log_item *iip = INODE_ITEM(lip);
486504
struct xfs_inode *ip = iip->ili_inode;
487-
struct xfs_buf *bp = NULL;
505+
struct xfs_buf *bp = lip->li_buf;
488506
uint rval = XFS_ITEM_SUCCESS;
489507
int error;
490508

491509
if (xfs_ipincount(ip) > 0)
492510
return XFS_ITEM_PINNED;
493511

512+
/*
513+
* The buffer containing this item failed to be written back
514+
* previously. Resubmit the buffer for IO.
515+
*/
516+
if (lip->li_flags & XFS_LI_FAILED) {
517+
if (!xfs_buf_trylock(bp))
518+
return XFS_ITEM_LOCKED;
519+
520+
if (!xfs_buf_resubmit_failed_buffers(bp, lip, buffer_list))
521+
rval = XFS_ITEM_FLUSHING;
522+
523+
xfs_buf_unlock(bp);
524+
return rval;
525+
}
526+
494527
if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED))
495528
return XFS_ITEM_LOCKED;
496529

@@ -622,7 +655,8 @@ static const struct xfs_item_ops xfs_inode_item_ops = {
622655
.iop_unlock = xfs_inode_item_unlock,
623656
.iop_committed = xfs_inode_item_committed,
624657
.iop_push = xfs_inode_item_push,
625-
.iop_committing = xfs_inode_item_committing
658+
.iop_committing = xfs_inode_item_committing,
659+
.iop_error = xfs_inode_item_error
626660
};
627661

628662

@@ -710,15 +744,17 @@ xfs_iflush_done(
710744
* the AIL lock.
711745
*/
712746
iip = INODE_ITEM(blip);
713-
if (iip->ili_logged && blip->li_lsn == iip->ili_flush_lsn)
747+
if ((iip->ili_logged && blip->li_lsn == iip->ili_flush_lsn) ||
748+
lip->li_flags & XFS_LI_FAILED)
714749
need_ail++;
715750

716751
blip = next;
717752
}
718753

719754
/* make sure we capture the state of the initial inode. */
720755
iip = INODE_ITEM(lip);
721-
if (iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn)
756+
if ((iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn) ||
757+
lip->li_flags & XFS_LI_FAILED)
722758
need_ail++;
723759

724760
/*
@@ -739,6 +775,9 @@ xfs_iflush_done(
739775
if (INODE_ITEM(blip)->ili_logged &&
740776
blip->li_lsn == INODE_ITEM(blip)->ili_flush_lsn)
741777
mlip_changed |= xfs_ail_delete_one(ailp, blip);
778+
else {
779+
xfs_clear_li_failed(blip);
780+
}
742781
}
743782

744783
if (mlip_changed) {

fs/xfs/xfs_trans.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ typedef struct xfs_log_item {
4949
struct xfs_ail *li_ailp; /* ptr to AIL */
5050
uint li_type; /* item type */
5151
uint li_flags; /* misc flags */
52+
struct xfs_buf *li_buf; /* real buffer pointer */
5253
struct xfs_log_item *li_bio_list; /* buffer item list */
5354
void (*li_cb)(struct xfs_buf *,
5455
struct xfs_log_item *);

fs/xfs/xfs_trans_ail.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,12 +687,13 @@ xfs_trans_ail_update_bulk(
687687
bool
688688
xfs_ail_delete_one(
689689
struct xfs_ail *ailp,
690-
struct xfs_log_item *lip)
690+
struct xfs_log_item *lip)
691691
{
692692
struct xfs_log_item *mlip = xfs_ail_min(ailp);
693693

694694
trace_xfs_ail_delete(lip, mlip->li_lsn, lip->li_lsn);
695695
xfs_ail_delete(ailp, lip);
696+
xfs_clear_li_failed(lip);
696697
lip->li_flags &= ~XFS_LI_IN_AIL;
697698
lip->li_lsn = 0;
698699

fs/xfs/xfs_trans_priv.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,35 @@ xfs_trans_ail_copy_lsn(
164164
*dst = *src;
165165
}
166166
#endif
167+
168+
static inline void
169+
xfs_clear_li_failed(
170+
struct xfs_log_item *lip)
171+
{
172+
struct xfs_buf *bp = lip->li_buf;
173+
174+
ASSERT(lip->li_flags & XFS_LI_IN_AIL);
175+
lockdep_assert_held(&lip->li_ailp->xa_lock);
176+
177+
if (lip->li_flags & XFS_LI_FAILED) {
178+
lip->li_flags &= ~XFS_LI_FAILED;
179+
lip->li_buf = NULL;
180+
xfs_buf_rele(bp);
181+
}
182+
}
183+
184+
static inline void
185+
xfs_set_li_failed(
186+
struct xfs_log_item *lip,
187+
struct xfs_buf *bp)
188+
{
189+
lockdep_assert_held(&lip->li_ailp->xa_lock);
190+
191+
if (!(lip->li_flags & XFS_LI_FAILED)) {
192+
xfs_buf_hold(bp);
193+
lip->li_flags |= XFS_LI_FAILED;
194+
lip->li_buf = bp;
195+
}
196+
}
197+
167198
#endif /* __XFS_TRANS_PRIV_H__ */

0 commit comments

Comments
 (0)