Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

xfs: don't commit sunit/swidth updates to disk if that would cause repair failures

Alex Lyakas reported[1] that mounting an xfs filesystem with new sunit
and swidth values could cause xfs_repair to fail loudly. The problem
here is that repair calculates the where mkfs should have allocated the
root inode, based on the superblock geometry. The allocation decisions
depend on sunit, which means that we really can't go updating sunit if
it would lead to a subsequent repair failure on an otherwise correct
filesystem.

Port from xfs_repair some code that computes the location of the root
inode and teach mount to skip the ondisk update if it would cause
problems for repair. Along the way we'll update the documentation,
provide a function for computing the minimum AGFL size instead of
open-coding it, and cut down some indenting in the mount code.

Note that we allow the mount to proceed (and new allocations will
reflect this new geometry) because we've never screened this kind of
thing before. We'll have to wait for a new future incompat feature to
enforce correct behavior, alas.

Note that the geometry reporting always uses the superblock values, not
the incore ones, so that is what xfs_info and xfs_growfs will report.

[1] https://lore.kernel.org/linux-xfs/20191125130744.GA44777@bfoster/T/#m00f9594b511e076e2fcdd489d78bc30216d72a7d

Reported-by: Alex Lyakas <alex@zadara.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>

+130 -1
+64
fs/xfs/libxfs/xfs_ialloc.c
··· 2909 2909 else 2910 2910 igeo->ialloc_align = 0; 2911 2911 } 2912 + 2913 + /* Compute the location of the root directory inode that is laid out by mkfs. */ 2914 + xfs_ino_t 2915 + xfs_ialloc_calc_rootino( 2916 + struct xfs_mount *mp, 2917 + int sunit) 2918 + { 2919 + struct xfs_ino_geometry *igeo = M_IGEO(mp); 2920 + xfs_agblock_t first_bno; 2921 + 2922 + /* 2923 + * Pre-calculate the geometry of AG 0. We know what it looks like 2924 + * because libxfs knows how to create allocation groups now. 2925 + * 2926 + * first_bno is the first block in which mkfs could possibly have 2927 + * allocated the root directory inode, once we factor in the metadata 2928 + * that mkfs formats before it. Namely, the four AG headers... 2929 + */ 2930 + first_bno = howmany(4 * mp->m_sb.sb_sectsize, mp->m_sb.sb_blocksize); 2931 + 2932 + /* ...the two free space btree roots... */ 2933 + first_bno += 2; 2934 + 2935 + /* ...the inode btree root... */ 2936 + first_bno += 1; 2937 + 2938 + /* ...the initial AGFL... */ 2939 + first_bno += xfs_alloc_min_freelist(mp, NULL); 2940 + 2941 + /* ...the free inode btree root... */ 2942 + if (xfs_sb_version_hasfinobt(&mp->m_sb)) 2943 + first_bno++; 2944 + 2945 + /* ...the reverse mapping btree root... */ 2946 + if (xfs_sb_version_hasrmapbt(&mp->m_sb)) 2947 + first_bno++; 2948 + 2949 + /* ...the reference count btree... */ 2950 + if (xfs_sb_version_hasreflink(&mp->m_sb)) 2951 + first_bno++; 2952 + 2953 + /* 2954 + * ...and the log, if it is allocated in the first allocation group. 2955 + * 2956 + * This can happen with filesystems that only have a single 2957 + * allocation group, or very odd geometries created by old mkfs 2958 + * versions on very small filesystems. 2959 + */ 2960 + if (mp->m_sb.sb_logstart && 2961 + XFS_FSB_TO_AGNO(mp, mp->m_sb.sb_logstart) == 0) 2962 + first_bno += mp->m_sb.sb_logblocks; 2963 + 2964 + /* 2965 + * Now round first_bno up to whatever allocation alignment is given 2966 + * by the filesystem or was passed in. 2967 + */ 2968 + if (xfs_sb_version_hasdalign(&mp->m_sb) && igeo->ialloc_align > 0) 2969 + first_bno = roundup(first_bno, sunit); 2970 + else if (xfs_sb_version_hasalign(&mp->m_sb) && 2971 + mp->m_sb.sb_inoalignmt > 1) 2972 + first_bno = roundup(first_bno, mp->m_sb.sb_inoalignmt); 2973 + 2974 + return XFS_AGINO_TO_INO(mp, 0, XFS_AGB_TO_AGINO(mp, first_bno)); 2975 + }
+1
fs/xfs/libxfs/xfs_ialloc.h
··· 152 152 153 153 int xfs_ialloc_cluster_alignment(struct xfs_mount *mp); 154 154 void xfs_ialloc_setup_geometry(struct xfs_mount *mp); 155 + xfs_ino_t xfs_ialloc_calc_rootino(struct xfs_mount *mp, int sunit); 155 156 156 157 #endif /* __XFS_IALLOC_H__ */
+44 -1
fs/xfs/xfs_mount.c
··· 31 31 #include "xfs_reflink.h" 32 32 #include "xfs_extent_busy.h" 33 33 #include "xfs_health.h" 34 - 34 + #include "xfs_trace.h" 35 35 36 36 static DEFINE_MUTEX(xfs_uuid_table_mutex); 37 37 static int xfs_uuid_table_size; ··· 360 360 } 361 361 362 362 /* 363 + * If the sunit/swidth change would move the precomputed root inode value, we 364 + * must reject the ondisk change because repair will stumble over that. 365 + * However, we allow the mount to proceed because we never rejected this 366 + * combination before. Returns true to update the sb, false otherwise. 367 + */ 368 + static inline int 369 + xfs_check_new_dalign( 370 + struct xfs_mount *mp, 371 + int new_dalign, 372 + bool *update_sb) 373 + { 374 + struct xfs_sb *sbp = &mp->m_sb; 375 + xfs_ino_t calc_ino; 376 + 377 + calc_ino = xfs_ialloc_calc_rootino(mp, new_dalign); 378 + trace_xfs_check_new_dalign(mp, new_dalign, calc_ino); 379 + 380 + if (sbp->sb_rootino == calc_ino) { 381 + *update_sb = true; 382 + return 0; 383 + } 384 + 385 + xfs_warn(mp, 386 + "Cannot change stripe alignment; would require moving root inode."); 387 + 388 + /* 389 + * XXX: Next time we add a new incompat feature, this should start 390 + * returning -EINVAL to fail the mount. Until then, spit out a warning 391 + * that we're ignoring the administrator's instructions. 392 + */ 393 + xfs_warn(mp, "Skipping superblock stripe alignment update."); 394 + *update_sb = false; 395 + return 0; 396 + } 397 + 398 + /* 363 399 * If we were provided with new sunit/swidth values as mount options, make sure 364 400 * that they pass basic alignment and superblock feature checks, and convert 365 401 * them into the same units (FSB) that everything else expects. This step ··· 455 419 struct xfs_sb *sbp = &mp->m_sb; 456 420 457 421 if (mp->m_dalign) { 422 + bool update_sb; 423 + int error; 424 + 458 425 if (sbp->sb_unit == mp->m_dalign && 459 426 sbp->sb_width == mp->m_swidth) 460 427 return 0; 428 + 429 + error = xfs_check_new_dalign(mp, mp->m_dalign, &update_sb); 430 + if (error || !update_sb) 431 + return error; 461 432 462 433 sbp->sb_unit = mp->m_dalign; 463 434 sbp->sb_width = mp->m_swidth;
+21
fs/xfs/xfs_trace.h
··· 3573 3573 DEFINE_KMEM_EVENT(kmem_realloc); 3574 3574 DEFINE_KMEM_EVENT(kmem_zone_alloc); 3575 3575 3576 + TRACE_EVENT(xfs_check_new_dalign, 3577 + TP_PROTO(struct xfs_mount *mp, int new_dalign, xfs_ino_t calc_rootino), 3578 + TP_ARGS(mp, new_dalign, calc_rootino), 3579 + TP_STRUCT__entry( 3580 + __field(dev_t, dev) 3581 + __field(int, new_dalign) 3582 + __field(xfs_ino_t, sb_rootino) 3583 + __field(xfs_ino_t, calc_rootino) 3584 + ), 3585 + TP_fast_assign( 3586 + __entry->dev = mp->m_super->s_dev; 3587 + __entry->new_dalign = new_dalign; 3588 + __entry->sb_rootino = mp->m_sb.sb_rootino; 3589 + __entry->calc_rootino = calc_rootino; 3590 + ), 3591 + TP_printk("dev %d:%d new_dalign %d sb_rootino %llu calc_rootino %llu", 3592 + MAJOR(__entry->dev), MINOR(__entry->dev), 3593 + __entry->new_dalign, __entry->sb_rootino, 3594 + __entry->calc_rootino) 3595 + ) 3596 + 3576 3597 #endif /* _TRACE_XFS_H */ 3577 3598 3578 3599 #undef TRACE_INCLUDE_PATH