This scenario is the source of much confusion for admins and support folks alike: # touch mnt/newfile touch: cannot touch ‘mnt/newfile’: No space left on device # df -h mnt Filesystem Size Used Avail Use% Mounted on /dev/loop0 196M 137M 59M 71% /tmp/mnt # df -i mnt/ Filesystem Inodes IUsed IFree IUse% Mounted on /dev/loop0 102400 64256 38144 63% /tmp/mnt because it appears that there is plenty of space available, yet ENOSPC is returned. Track this case in the allocation args structure, and when an allocation fails due to alignment constraints, leave a clue in the kernel logs: XFS (loop0): Failed metadata allocation due to 4-block alignment constraint Signed-off-by: Eric Sandeen <sandeen@xxxxxxxxxx> --- Right now this depends on my "printk_once" patch but you can change xfs_warn_once to xfs_warn or xfs_warn_ratelimited for testing. Perhaps a 2nd patch to log a similar message if alignment failed due to /contiguous/ free space constraints would be good as well? diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index 203e74fa64aa..10f32797e5ca 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -2303,8 +2303,12 @@ xfs_alloc_space_available( /* do we have enough contiguous free space for the allocation? */ alloc_len = args->minlen + (args->alignment - 1) + args->minalignslop; longest = xfs_alloc_longest_free_extent(pag, min_free, reservation); - if (longest < alloc_len) + if (longest < alloc_len) { + /* Did we fail only due to alignment? */ + if (longest >= args->minlen) + args->alignfail = 1; return false; + } /* * Do we have enough free space remaining for the allocation? Don't @@ -3067,8 +3071,10 @@ xfs_alloc_vextent( agsize = mp->m_sb.sb_agblocks; if (args->maxlen > agsize) args->maxlen = agsize; - if (args->alignment == 0) + if (args->alignment == 0) { args->alignment = 1; + args->alignfail = 0; + } ASSERT(XFS_FSB_TO_AGNO(mp, args->fsbno) < mp->m_sb.sb_agcount); ASSERT(XFS_FSB_TO_AGBNO(mp, args->fsbno) < agsize); ASSERT(args->minlen <= args->maxlen); @@ -3227,6 +3233,13 @@ xfs_alloc_vextent( } xfs_perag_put(args->pag); + if (!args->agbp && args->alignment > 1 && args->alignfail) { + xfs_warn_once(args->mp, +"Failed %s allocation due to %u-block alignment constraint", + XFS_RMAP_NON_INODE_OWNER(args->oinfo.oi_owner) ? + "metadata" : "data", + args->alignment); + } return 0; error0: xfs_perag_put(args->pag); diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h index a851bf77f17b..29d13cd5c9ac 100644 --- a/fs/xfs/libxfs/xfs_alloc.h +++ b/fs/xfs/libxfs/xfs_alloc.h @@ -73,6 +73,7 @@ typedef struct xfs_alloc_arg { int datatype; /* mask defining data type treatment */ char wasdel; /* set if allocation was prev delayed */ char wasfromfl; /* set if allocation is from freelist */ + char alignfail; /* set if alloc failed due to alignmt */ struct xfs_owner_info oinfo; /* owner of blocks being allocated */ enum xfs_ag_resv_type resv; /* block reservation to use */ } xfs_alloc_arg_t; diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index fda13cd7add0..808060649cad 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -3563,6 +3563,7 @@ xfs_bmap_btalloc( args.mp = mp; args.fsbno = ap->blkno; args.oinfo = XFS_RMAP_OINFO_SKIP_UPDATE; + args.alignfail = 0; /* Trim the allocation back to the maximum an AG can fit. */ args.maxlen = min(ap->length, mp->m_ag_max_usable); diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index 7fcf62b324b0..e98dcb8e65eb 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -685,6 +685,7 @@ xfs_ialloc_ag_alloc( * but not to use them in the actual exact allocation. */ args.alignment = 1; + args.alignfail = 0; args.minalignslop = igeo->cluster_align - 1; /* Allow space for the inode btree to split. */