]> Pileus Git - ~andy/linux/blobdiff - fs/xfs/xfs_alloc.c
arm64: perf: fix array out of bounds access in armpmu_map_hw_event()
[~andy/linux] / fs / xfs / xfs_alloc.c
index 5673bcfda2f0c8c0151142b52dc5202d7030da63..71596e57283ae6b44702f8d6866405de6b911728 100644 (file)
@@ -175,6 +175,7 @@ xfs_alloc_compute_diff(
        xfs_agblock_t   wantbno,        /* target starting block */
        xfs_extlen_t    wantlen,        /* target length */
        xfs_extlen_t    alignment,      /* target alignment */
+       char            userdata,       /* are we allocating data? */
        xfs_agblock_t   freebno,        /* freespace's starting block */
        xfs_extlen_t    freelen,        /* freespace's length */
        xfs_agblock_t   *newbnop)       /* result: best start block from free */
@@ -189,7 +190,14 @@ xfs_alloc_compute_diff(
        ASSERT(freelen >= wantlen);
        freeend = freebno + freelen;
        wantend = wantbno + wantlen;
-       if (freebno >= wantbno) {
+       /*
+        * We want to allocate from the start of a free extent if it is past
+        * the desired block or if we are allocating user data and the free
+        * extent is before desired block. The second case is there to allow
+        * for contiguous allocation from the remaining free space if the file
+        * grows in the short term.
+        */
+       if (freebno >= wantbno || (userdata && freeend < wantend)) {
                if ((newbno1 = roundup(freebno, alignment)) >= freeend)
                        newbno1 = NULLAGBLOCK;
        } else if (freeend >= wantend && alignment > 1) {
@@ -805,7 +813,8 @@ xfs_alloc_find_best_extent(
                        xfs_alloc_fix_len(args);
 
                        sdiff = xfs_alloc_compute_diff(args->agbno, args->len,
-                                                      args->alignment, *sbnoa,
+                                                      args->alignment,
+                                                      args->userdata, *sbnoa,
                                                       *slena, &new);
 
                        /*
@@ -976,7 +985,8 @@ restart:
                        if (args->len < blen)
                                continue;
                        ltdiff = xfs_alloc_compute_diff(args->agbno, args->len,
-                               args->alignment, ltbnoa, ltlena, &ltnew);
+                               args->alignment, args->userdata, ltbnoa,
+                               ltlena, &ltnew);
                        if (ltnew != NULLAGBLOCK &&
                            (args->len > blen || ltdiff < bdiff)) {
                                bdiff = ltdiff;
@@ -1128,7 +1138,8 @@ restart:
                        args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen);
                        xfs_alloc_fix_len(args);
                        ltdiff = xfs_alloc_compute_diff(args->agbno, args->len,
-                               args->alignment, ltbnoa, ltlena, &ltnew);
+                               args->alignment, args->userdata, ltbnoa,
+                               ltlena, &ltnew);
 
                        error = xfs_alloc_find_best_extent(args,
                                                &bno_cur_lt, &bno_cur_gt,
@@ -1144,7 +1155,8 @@ restart:
                        args->len = XFS_EXTLEN_MIN(gtlena, args->maxlen);
                        xfs_alloc_fix_len(args);
                        gtdiff = xfs_alloc_compute_diff(args->agbno, args->len,
-                               args->alignment, gtbnoa, gtlena, &gtnew);
+                               args->alignment, args->userdata, gtbnoa,
+                               gtlena, &gtnew);
 
                        error = xfs_alloc_find_best_extent(args,
                                                &bno_cur_gt, &bno_cur_lt,
@@ -1203,7 +1215,7 @@ restart:
        }
        rlen = args->len;
        (void)xfs_alloc_compute_diff(args->agbno, rlen, args->alignment,
-                                    ltbnoa, ltlena, &ltnew);
+                                    args->userdata, ltbnoa, ltlena, &ltnew);
        ASSERT(ltnew >= ltbno);
        ASSERT(ltnew + rlen <= ltbnoa + ltlena);
        ASSERT(ltnew + rlen <= be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length));