From: Darrick J. Wong <djwong@xxxxxxxxxx> Make the userspace xfs_alloc_file_space behave (more or less) like the kernel version, at least as far as the interface goes. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- include/libxfs.h | 4 +- libxfs/util.c | 140 ++++++++++++++++++++++++++++++++++++------------------ mkfs/proto.c | 2 - 3 files changed, 96 insertions(+), 50 deletions(-) diff --git a/include/libxfs.h b/include/libxfs.h index 348d36be192966..878fefbbde7524 100644 --- a/include/libxfs.h +++ b/include/libxfs.h @@ -176,8 +176,8 @@ extern int libxfs_log_header(char *, uuid_t *, int, int, int, xfs_lsn_t, /* Shared utility routines */ -extern int libxfs_alloc_file_space (struct xfs_inode *, xfs_off_t, - xfs_off_t, int, int); +int libxfs_alloc_file_space(struct xfs_inode *ip, xfs_off_t offset, + xfs_off_t len, uint32_t bmapi_flags); /* XXX: this is messy and needs fixing */ #ifndef __LIBXFS_INTERNAL_XFS_H__ diff --git a/libxfs/util.c b/libxfs/util.c index 97da94506aee01..e5892fc86c3e92 100644 --- a/libxfs/util.c +++ b/libxfs/util.c @@ -179,78 +179,124 @@ libxfs_mod_incore_sb( */ int libxfs_alloc_file_space( - xfs_inode_t *ip, - xfs_off_t offset, - xfs_off_t len, - int alloc_type, - int attr_flags) + struct xfs_inode *ip, + xfs_off_t offset, + xfs_off_t len, + uint32_t bmapi_flags) { - xfs_mount_t *mp; - xfs_off_t count; - xfs_filblks_t datablocks; - xfs_filblks_t allocated_fsb; - xfs_filblks_t allocatesize_fsb; - xfs_bmbt_irec_t *imapp; - xfs_bmbt_irec_t imaps[1]; - int reccount; - uint resblks; - xfs_fileoff_t startoffset_fsb; - xfs_trans_t *tp; - int xfs_bmapi_flags; - int error; + xfs_mount_t *mp = ip->i_mount; + xfs_off_t count; + xfs_filblks_t allocatesize_fsb; + xfs_extlen_t extsz, temp; + xfs_fileoff_t startoffset_fsb; + xfs_fileoff_t endoffset_fsb; + int rt; + xfs_trans_t *tp; + xfs_bmbt_irec_t imaps[1], *imapp; + int error; if (len <= 0) return -EINVAL; + rt = XFS_IS_REALTIME_INODE(ip); + extsz = xfs_get_extsz_hint(ip); + count = len; - error = 0; imapp = &imaps[0]; - reccount = 1; - xfs_bmapi_flags = alloc_type ? XFS_BMAPI_PREALLOC : 0; - mp = ip->i_mount; - startoffset_fsb = XFS_B_TO_FSBT(mp, offset); - allocatesize_fsb = XFS_B_TO_FSB(mp, count); + startoffset_fsb = XFS_B_TO_FSBT(mp, offset); + endoffset_fsb = XFS_B_TO_FSB(mp, offset + count); + allocatesize_fsb = endoffset_fsb - startoffset_fsb; - /* allocate file space until done or until there is an error */ + /* + * Allocate file space until done or until there is an error + */ while (allocatesize_fsb && !error) { - datablocks = allocatesize_fsb; + xfs_fileoff_t s, e; + unsigned int dblocks, rblocks, resblks; + int nimaps = 1; - resblks = (uint)XFS_DIOSTRAT_SPACE_RES(mp, datablocks); - error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, - 0, 0, &tp); /* - * Check for running out of space + * Determine space reservations for data/realtime. */ - if (error) { - ASSERT(error == -ENOSPC); + if (unlikely(extsz)) { + s = startoffset_fsb; + do_div(s, extsz); + s *= extsz; + e = startoffset_fsb + allocatesize_fsb; + div_u64_rem(startoffset_fsb, extsz, &temp); + if (temp) + e += temp; + div_u64_rem(e, extsz, &temp); + if (temp) + e += extsz - temp; + } else { + s = 0; + e = allocatesize_fsb; + } + + /* + * The transaction reservation is limited to a 32-bit block + * count, hence we need to limit the number of blocks we are + * trying to reserve to avoid an overflow. We can't allocate + * more than @nimaps extents, and an extent is limited on disk + * to XFS_BMBT_MAX_EXTLEN (21 bits), so use that to enforce the + * limit. + */ + resblks = min_t(xfs_fileoff_t, (e - s), + (XFS_MAX_BMBT_EXTLEN * nimaps)); + if (unlikely(rt)) { + dblocks = XFS_DIOSTRAT_SPACE_RES(mp, 0); + rblocks = resblks; + } else { + dblocks = XFS_DIOSTRAT_SPACE_RES(mp, resblks); + rblocks = 0; + } + + error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_write, + dblocks, rblocks, false, &tp); + if (error) break; - } - xfs_trans_ijoin(tp, ip, 0); - error = xfs_bmapi_write(tp, ip, startoffset_fsb, allocatesize_fsb, - xfs_bmapi_flags, 0, imapp, &reccount); + error = xfs_iext_count_extend(tp, ip, XFS_DATA_FORK, + XFS_IEXT_ADD_NOSPLIT_CNT); + if (error) + goto error; + error = xfs_bmapi_write(tp, ip, startoffset_fsb, + allocatesize_fsb, bmapi_flags, 0, imapp, + &nimaps); if (error) - goto error0; + goto error; + + ip->i_diflags |= XFS_DIFLAG_PREALLOC; + xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); - /* - * Complete the transaction - */ error = xfs_trans_commit(tp); + xfs_iunlock(ip, XFS_ILOCK_EXCL); if (error) break; - allocated_fsb = imapp->br_blockcount; - if (reccount == 0) - return -ENOSPC; - - startoffset_fsb += allocated_fsb; - allocatesize_fsb -= allocated_fsb; + /* + * If xfs_bmapi_write finds a delalloc extent at the requested + * range, it tries to convert the entire delalloc extent to a + * real allocation. + * If the allocator cannot find a single free extent large + * enough to cover the start block of the requested range, + * xfs_bmapi_write will return 0 but leave *nimaps set to 0. + * In that case we simply need to keep looping with the same + * startoffset_fsb. + */ + if (nimaps) { + startoffset_fsb += imapp->br_blockcount; + allocatesize_fsb -= imapp->br_blockcount; + } } + return error; -error0: /* Cancel bmap, cancel trans */ +error: xfs_trans_cancel(tp); + xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; } diff --git a/mkfs/proto.c b/mkfs/proto.c index 05c2621f8a0b13..0764064e043e97 100644 --- a/mkfs/proto.c +++ b/mkfs/proto.c @@ -203,7 +203,7 @@ rsvfile( int error; xfs_trans_t *tp; - error = -libxfs_alloc_file_space(ip, 0, llen, 1, 0); + error = -libxfs_alloc_file_space(ip, 0, llen, XFS_BMAPI_PREALLOC); if (error) { fail(_("error reserving space for a file"), error);