]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
libxfs: resync libxfs_alloc_file_space interface with the kernel
authorDarrick J. Wong <djwong@kernel.org>
Tue, 9 Jan 2024 17:40:22 +0000 (09:40 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 10 Apr 2024 00:21:45 +0000 (17:21 -0700)
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@kernel.org>
include/libxfs.h
libxfs/util.c
mkfs/proto.c

index 58305479022639ae4738f71c46784494627ee9b6..88112bb7839d2f8a45d6c30297ab5670e42c3d44 100644 (file)
@@ -178,8 +178,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);
+extern 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__
index 76e49b8637c59c4b14a5695c1fa2e0de449ff09c..aec7798a8149c099fe91259e242fa96f502628f6 100644 (file)
@@ -179,78 +179,127 @@ 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);
-                       break;
+               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;
                }
-               xfs_trans_ijoin(tp, ip, 0);
 
-               error = xfs_bmapi_write(tp, ip, startoffset_fsb, allocatesize_fsb,
-                               xfs_bmapi_flags, 0, imapp, &reccount);
+               /*
+                * 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)
-                       goto error0;
+                       break;
+
+               error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK,
+                               XFS_IEXT_ADD_NOSPLIT_CNT);
+               if (error == -EFBIG)
+                       error = xfs_iext_count_upgrade(tp, ip,
+                                       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 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;
 }
 
index 4eea8cfa6e1819f70af645b06e3deda64a4b15da..742ad5be4853bb2408c5a317267d578e77e3624f 100644 (file)
@@ -205,7 +205,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);