Merge tag 'omap-for-v5.13/fixes-pm' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / fs / xfs / xfs_bmap_util.c
index a5e9d7d..0936f3a 100644 (file)
@@ -71,18 +71,24 @@ xfs_zero_extent(
 #ifdef CONFIG_XFS_RT
 int
 xfs_bmap_rtalloc(
-       struct xfs_bmalloca     *ap)    /* bmap alloc argument struct */
+       struct xfs_bmalloca     *ap)
 {
-       int             error;          /* error return value */
-       xfs_mount_t     *mp;            /* mount point structure */
-       xfs_extlen_t    prod = 0;       /* product factor for allocators */
-       xfs_extlen_t    mod = 0;        /* product factor for allocators */
-       xfs_extlen_t    ralen = 0;      /* realtime allocation length */
-       xfs_extlen_t    align;          /* minimum allocation alignment */
-       xfs_rtblock_t   rtb;
-
-       mp = ap->ip->i_mount;
+       struct xfs_mount        *mp = ap->ip->i_mount;
+       xfs_fileoff_t           orig_offset = ap->offset;
+       xfs_rtblock_t           rtb;
+       xfs_extlen_t            prod = 0;  /* product factor for allocators */
+       xfs_extlen_t            mod = 0;   /* product factor for allocators */
+       xfs_extlen_t            ralen = 0; /* realtime allocation length */
+       xfs_extlen_t            align;     /* minimum allocation alignment */
+       xfs_extlen_t            orig_length = ap->length;
+       xfs_extlen_t            minlen = mp->m_sb.sb_rextsize;
+       xfs_extlen_t            raminlen;
+       bool                    rtlocked = false;
+       bool                    ignore_locality = false;
+       int                     error;
+
        align = xfs_get_extsz_hint(ap->ip);
+retry:
        prod = align / mp->m_sb.sb_rextsize;
        error = xfs_bmap_extsize_align(mp, &ap->got, &ap->prev,
                                        align, 1, ap->eof, 0,
@@ -92,6 +98,15 @@ xfs_bmap_rtalloc(
        ASSERT(ap->length);
        ASSERT(ap->length % mp->m_sb.sb_rextsize == 0);
 
+       /*
+        * If we shifted the file offset downward to satisfy an extent size
+        * hint, increase minlen by that amount so that the allocator won't
+        * give us an allocation that's too short to cover at least one of the
+        * blocks that the caller asked for.
+        */
+       if (ap->offset != orig_offset)
+               minlen += orig_offset - ap->offset;
+
        /*
         * If the offset & length are not perfectly aligned
         * then kill prod, it will just get us in trouble.
@@ -116,10 +131,13 @@ xfs_bmap_rtalloc(
        /*
         * Lock out modifications to both the RT bitmap and summary inodes
         */
-       xfs_ilock(mp->m_rbmip, XFS_ILOCK_EXCL|XFS_ILOCK_RTBITMAP);
-       xfs_trans_ijoin(ap->tp, mp->m_rbmip, XFS_ILOCK_EXCL);
-       xfs_ilock(mp->m_rsumip, XFS_ILOCK_EXCL|XFS_ILOCK_RTSUM);
-       xfs_trans_ijoin(ap->tp, mp->m_rsumip, XFS_ILOCK_EXCL);
+       if (!rtlocked) {
+               xfs_ilock(mp->m_rbmip, XFS_ILOCK_EXCL|XFS_ILOCK_RTBITMAP);
+               xfs_trans_ijoin(ap->tp, mp->m_rbmip, XFS_ILOCK_EXCL);
+               xfs_ilock(mp->m_rsumip, XFS_ILOCK_EXCL|XFS_ILOCK_RTSUM);
+               xfs_trans_ijoin(ap->tp, mp->m_rsumip, XFS_ILOCK_EXCL);
+               rtlocked = true;
+       }
 
        /*
         * If it's an allocation to an empty file at offset 0,
@@ -141,33 +159,59 @@ xfs_bmap_rtalloc(
        /*
         * Realtime allocation, done through xfs_rtallocate_extent.
         */
-       do_div(ap->blkno, mp->m_sb.sb_rextsize);
+       if (ignore_locality)
+               ap->blkno = 0;
+       else
+               do_div(ap->blkno, mp->m_sb.sb_rextsize);
        rtb = ap->blkno;
        ap->length = ralen;
-       error = xfs_rtallocate_extent(ap->tp, ap->blkno, 1, ap->length,
-                               &ralen, ap->wasdel, prod, &rtb);
+       raminlen = max_t(xfs_extlen_t, 1, minlen / mp->m_sb.sb_rextsize);
+       error = xfs_rtallocate_extent(ap->tp, ap->blkno, raminlen, ap->length,
+                       &ralen, ap->wasdel, prod, &rtb);
        if (error)
                return error;
 
-       ap->blkno = rtb;
-       if (ap->blkno != NULLFSBLOCK) {
-               ap->blkno *= mp->m_sb.sb_rextsize;
-               ralen *= mp->m_sb.sb_rextsize;
-               ap->length = ralen;
-               ap->ip->i_nblocks += ralen;
+       if (rtb != NULLRTBLOCK) {
+               ap->blkno = rtb * mp->m_sb.sb_rextsize;
+               ap->length = ralen * mp->m_sb.sb_rextsize;
+               ap->ip->i_nblocks += ap->length;
                xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
                if (ap->wasdel)
-                       ap->ip->i_delayed_blks -= ralen;
+                       ap->ip->i_delayed_blks -= ap->length;
                /*
                 * Adjust the disk quota also. This was reserved
                 * earlier.
                 */
                xfs_trans_mod_dquot_byino(ap->tp, ap->ip,
                        ap->wasdel ? XFS_TRANS_DQ_DELRTBCOUNT :
-                                       XFS_TRANS_DQ_RTBCOUNT, (long) ralen);
-       } else {
-               ap->length = 0;
+                                       XFS_TRANS_DQ_RTBCOUNT, ap->length);
+               return 0;
        }
+
+       if (align > mp->m_sb.sb_rextsize) {
+               /*
+                * We previously enlarged the request length to try to satisfy
+                * an extent size hint.  The allocator didn't return anything,
+                * so reset the parameters to the original values and try again
+                * without alignment criteria.
+                */
+               ap->offset = orig_offset;
+               ap->length = orig_length;
+               minlen = align = mp->m_sb.sb_rextsize;
+               goto retry;
+       }
+
+       if (!ignore_locality && ap->blkno != 0) {
+               /*
+                * If we can't allocate near a specific rt extent, try again
+                * without locality criteria.
+                */
+               ignore_locality = true;
+               goto retry;
+       }
+
+       ap->blkno = NULLFSBLOCK;
+       ap->length = 0;
        return 0;
 }
 #endif /* CONFIG_XFS_RT */