diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index a718bb002cf4..ca3cf3f6239a 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -191,6 +191,25 @@ xfs_rmaproot_init( } } +/* + * Initialise new secondary superblocks with the pre-grow geometry, but mark + * them as "in progress" so we know they haven't yet been activated. This will + * get cleared when the update with the new geometry information is done after + * changes to the primary are committed. This isn't strictly necessary, but we + * get it for free with the delayed buffer write lists and it means we can tell + * if a grow operation didn't complete properly after the fact. + */ +static void +xfs_sbblock_init( + struct xfs_mount *mp, + struct xfs_buf *bp, + struct aghdr_init_data *id) +{ + struct xfs_dsb *dsb = XFS_BUF_TO_SBP(bp); + + xfs_sb_to_disk(dsb, &mp->m_sb); + dsb->sb_inprogress = 1; +} static void xfs_agfblock_init( @@ -326,6 +345,13 @@ xfs_grow_ag_headers( { struct xfs_aghdr_grow_data aghdr_data[] = { + { /* SB */ + .daddr = XFS_AG_DADDR(mp, id->agno, XFS_SB_DADDR), + .numblks = XFS_FSS_TO_BB(mp, 1), + .ops = &xfs_sb_buf_ops, + .work = &xfs_sbblock_init, + .need_init = true + }, { /* AGF */ .daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp)), .numblks = XFS_FSS_TO_BB(mp, 1), @@ -658,43 +684,30 @@ xfs_growfs_imaxpct( /* * After a grow operation, we need to update all the secondary superblocks - * to match the new state of the primary. Read/init the superblocks and update - * them appropriately. + * to match the new state of the primary. Because we are completely overwriting + * all the existing fields in the secondary superblock buffers, there is no need + * to read them in from disk. Just get a new buffer, stamp it and write it. + * + * The sb buffers need to be cached here so that we serialise against scrub + * scanning secondary superblocks, but we don't want to keep it in memory once + * it is written so we mark it as a one-shot buffer. */ static int xfs_growfs_update_superblocks( - struct xfs_mount *mp, - xfs_agnumber_t oagcount) + struct xfs_mount *mp) { - struct xfs_buf *bp; xfs_agnumber_t agno; int saved_error = 0; int error = 0; + LIST_HEAD (buffer_list); /* update secondary superblocks. */ for (agno = 1; agno < mp->m_sb.sb_agcount; agno++) { - error = 0; - /* - * new secondary superblocks need to be zeroed, not read from - * disk as the contents of the new area we are growing into is - * completely unknown. - */ - if (agno < oagcount) { - error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, - XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)), - XFS_FSS_TO_BB(mp, 1), 0, &bp, - &xfs_sb_buf_ops); - } else { - bp = xfs_trans_get_buf(NULL, mp->m_ddev_targp, - XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)), - XFS_FSS_TO_BB(mp, 1), 0); - if (bp) { - bp->b_ops = &xfs_sb_buf_ops; - xfs_buf_zero(bp, 0, BBTOB(bp->b_length)); - } else - error = -ENOMEM; - } + struct xfs_buf *bp; + bp = xfs_buf_get(mp->m_ddev_targp, + XFS_AG_DADDR(mp, agno, XFS_SB_DADDR), + XFS_FSS_TO_BB(mp, 1), 0); /* * If we get an error reading or writing alternate superblocks, * continue. xfs_repair chooses the "best" superblock based @@ -702,25 +715,42 @@ xfs_growfs_update_superblocks( * superblocks un-updated than updated, and xfs_repair may * pick them over the properly-updated primary. */ - if (error) { + if (!bp) { xfs_warn(mp, - "error %d reading secondary superblock for ag %d", - error, agno); - saved_error = error; + "error allocating secondary superblock for ag %d", + agno); + if (!saved_error) + saved_error = -ENOMEM; continue; } - xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb); - error = xfs_bwrite(bp); + bp->b_ops = &xfs_sb_buf_ops; + xfs_buf_oneshot(bp); + xfs_buf_zero(bp, 0, BBTOB(bp->b_length)); + xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb); + xfs_buf_delwri_queue(bp, &buffer_list); xfs_buf_relse(bp); + + /* don't hold too many buffers at once */ + if (agno % 16) + continue; + + error = xfs_buf_delwri_submit(&buffer_list); if (error) { xfs_warn(mp, - "write error %d updating secondary superblock for ag %d", + "write error %d updating a secondary superblock near ag %d", error, agno); - saved_error = error; + if (!saved_error) + saved_error = error; continue; } } + error = xfs_buf_delwri_submit(&buffer_list); + if (error) { + xfs_warn(mp, + "write error %d updating a secondary superblock near ag %d", + error, agno); + } return saved_error ? saved_error : error; } @@ -735,7 +765,6 @@ xfs_growfs_data( struct xfs_mount *mp, struct xfs_growfs_data *in) { - xfs_agnumber_t oagcount; int error = 0; if (!capable(CAP_SYS_ADMIN)) @@ -750,7 +779,6 @@ xfs_growfs_data( goto out_error; } - oagcount = mp->m_sb.sb_agcount; if (in->newblocks != mp->m_sb.sb_dblocks) { error = xfs_growfs_data_private(mp, in); if (error) @@ -766,7 +794,7 @@ xfs_growfs_data( mp->m_maxicount = 0; /* Update secondary superblocks now the physical grow has completed */ - error = xfs_growfs_update_superblocks(mp, oagcount); + error = xfs_growfs_update_superblocks(mp); out_error: /*