Merge branch 'work.d_name' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-2.6-microblaze.git] / fs / xfs / libxfs / xfs_bmap.c
index bc44641..e0905ad 100644 (file)
@@ -1079,21 +1079,13 @@ xfs_bmap_add_attrfork(
 
        blks = XFS_ADDAFORK_SPACE_RES(mp);
 
-       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_addafork, blks, 0,
-                       rsvd ? XFS_TRANS_RESERVE : 0, &tp);
+       error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_addafork, blks, 0,
+                       rsvd, &tp);
        if (error)
                return error;
-
-       xfs_ilock(ip, XFS_ILOCK_EXCL);
-       error = xfs_trans_reserve_quota_nblks(tp, ip, blks, 0, rsvd ?
-                       XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES :
-                       XFS_QMOPT_RES_REGBLKS);
-       if (error)
-               goto trans_cancel;
        if (XFS_IFORK_Q(ip))
                goto trans_cancel;
 
-       xfs_trans_ijoin(tp, ip, 0);
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
        error = xfs_bmap_set_attrforkoff(ip, size, &version);
        if (error)
@@ -3463,34 +3455,16 @@ xfs_bmap_btalloc_accounting(
                args->len);
 }
 
-STATIC int
-xfs_bmap_btalloc(
-       struct xfs_bmalloca     *ap)    /* bmap alloc argument struct */
+static int
+xfs_bmap_compute_alignments(
+       struct xfs_bmalloca     *ap,
+       struct xfs_alloc_arg    *args)
 {
-       xfs_mount_t     *mp;            /* mount point structure */
-       xfs_alloctype_t atype = 0;      /* type for allocation routines */
-       xfs_extlen_t    align = 0;      /* minimum allocation alignment */
-       xfs_agnumber_t  fb_agno;        /* ag number of ap->firstblock */
-       xfs_agnumber_t  ag;
-       xfs_alloc_arg_t args;
-       xfs_fileoff_t   orig_offset;
-       xfs_extlen_t    orig_length;
-       xfs_extlen_t    blen;
-       xfs_extlen_t    nextminlen = 0;
-       int             nullfb;         /* true if ap->firstblock isn't set */
-       int             isaligned;
-       int             tryagain;
-       int             error;
-       int             stripe_align;
-
-       ASSERT(ap->length);
-       orig_offset = ap->offset;
-       orig_length = ap->length;
-
-       mp = ap->ip->i_mount;
+       struct xfs_mount        *mp = args->mp;
+       xfs_extlen_t            align = 0; /* minimum allocation alignment */
+       int                     stripe_align = 0;
 
        /* stripe alignment for allocation is determined by mount parameters */
-       stripe_align = 0;
        if (mp->m_swidth && (mp->m_flags & XFS_MOUNT_SWALLOC))
                stripe_align = mp->m_swidth;
        else if (mp->m_dalign)
@@ -3501,13 +3475,171 @@ xfs_bmap_btalloc(
        else if (ap->datatype & XFS_ALLOC_USERDATA)
                align = xfs_get_extsz_hint(ap->ip);
        if (align) {
-               error = xfs_bmap_extsize_align(mp, &ap->got, &ap->prev,
-                                               align, 0, ap->eof, 0, ap->conv,
-                                               &ap->offset, &ap->length);
-               ASSERT(!error);
+               if (xfs_bmap_extsize_align(mp, &ap->got, &ap->prev, align, 0,
+                                       ap->eof, 0, ap->conv, &ap->offset,
+                                       &ap->length))
+                       ASSERT(0);
                ASSERT(ap->length);
        }
 
+       /* apply extent size hints if obtained earlier */
+       if (align) {
+               args->prod = align;
+               div_u64_rem(ap->offset, args->prod, &args->mod);
+               if (args->mod)
+                       args->mod = args->prod - args->mod;
+       } else if (mp->m_sb.sb_blocksize >= PAGE_SIZE) {
+               args->prod = 1;
+               args->mod = 0;
+       } else {
+               args->prod = PAGE_SIZE >> mp->m_sb.sb_blocklog;
+               div_u64_rem(ap->offset, args->prod, &args->mod);
+               if (args->mod)
+                       args->mod = args->prod - args->mod;
+       }
+
+       return stripe_align;
+}
+
+static void
+xfs_bmap_process_allocated_extent(
+       struct xfs_bmalloca     *ap,
+       struct xfs_alloc_arg    *args,
+       xfs_fileoff_t           orig_offset,
+       xfs_extlen_t            orig_length)
+{
+       int                     nullfb;
+
+       nullfb = ap->tp->t_firstblock == NULLFSBLOCK;
+
+       /*
+        * check the allocation happened at the same or higher AG than
+        * the first block that was allocated.
+        */
+       ASSERT(nullfb ||
+               XFS_FSB_TO_AGNO(args->mp, ap->tp->t_firstblock) <=
+               XFS_FSB_TO_AGNO(args->mp, args->fsbno));
+
+       ap->blkno = args->fsbno;
+       if (nullfb)
+               ap->tp->t_firstblock = args->fsbno;
+       ap->length = args->len;
+       /*
+        * If the extent size hint is active, we tried to round the
+        * caller's allocation request offset down to extsz and the
+        * length up to another extsz boundary.  If we found a free
+        * extent we mapped it in starting at this new offset.  If the
+        * newly mapped space isn't long enough to cover any of the
+        * range of offsets that was originally requested, move the
+        * mapping up so that we can fill as much of the caller's
+        * original request as possible.  Free space is apparently
+        * very fragmented so we're unlikely to be able to satisfy the
+        * hints anyway.
+        */
+       if (ap->length <= orig_length)
+               ap->offset = orig_offset;
+       else if (ap->offset + ap->length < orig_offset + orig_length)
+               ap->offset = orig_offset + orig_length - ap->length;
+       xfs_bmap_btalloc_accounting(ap, args);
+}
+
+#ifdef DEBUG
+static int
+xfs_bmap_exact_minlen_extent_alloc(
+       struct xfs_bmalloca     *ap)
+{
+       struct xfs_mount        *mp = ap->ip->i_mount;
+       struct xfs_alloc_arg    args = { .tp = ap->tp, .mp = mp };
+       xfs_fileoff_t           orig_offset;
+       xfs_extlen_t            orig_length;
+       int                     error;
+
+       ASSERT(ap->length);
+
+       if (ap->minlen != 1) {
+               ap->blkno = NULLFSBLOCK;
+               ap->length = 0;
+               return 0;
+       }
+
+       orig_offset = ap->offset;
+       orig_length = ap->length;
+
+       args.alloc_minlen_only = 1;
+
+       xfs_bmap_compute_alignments(ap, &args);
+
+       if (ap->tp->t_firstblock == NULLFSBLOCK) {
+               /*
+                * Unlike the longest extent available in an AG, we don't track
+                * the length of an AG's shortest extent.
+                * XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT is a debug only knob and
+                * hence we can afford to start traversing from the 0th AG since
+                * we need not be concerned about a drop in performance in
+                * "debug only" code paths.
+                */
+               ap->blkno = XFS_AGB_TO_FSB(mp, 0, 0);
+       } else {
+               ap->blkno = ap->tp->t_firstblock;
+       }
+
+       args.fsbno = ap->blkno;
+       args.oinfo = XFS_RMAP_OINFO_SKIP_UPDATE;
+       args.type = XFS_ALLOCTYPE_FIRST_AG;
+       args.total = args.minlen = args.maxlen = ap->minlen;
+
+       args.alignment = 1;
+       args.minalignslop = 0;
+
+       args.minleft = ap->minleft;
+       args.wasdel = ap->wasdel;
+       args.resv = XFS_AG_RESV_NONE;
+       args.datatype = ap->datatype;
+
+       error = xfs_alloc_vextent(&args);
+       if (error)
+               return error;
+
+       if (args.fsbno != NULLFSBLOCK) {
+               xfs_bmap_process_allocated_extent(ap, &args, orig_offset,
+                       orig_length);
+       } else {
+               ap->blkno = NULLFSBLOCK;
+               ap->length = 0;
+       }
+
+       return 0;
+}
+#else
+
+#define xfs_bmap_exact_minlen_extent_alloc(bma) (-EFSCORRUPTED)
+
+#endif
+
+STATIC int
+xfs_bmap_btalloc(
+       struct xfs_bmalloca     *ap)
+{
+       struct xfs_mount        *mp = ap->ip->i_mount;
+       struct xfs_alloc_arg    args = { .tp = ap->tp, .mp = mp };
+       xfs_alloctype_t         atype = 0;
+       xfs_agnumber_t          fb_agno;        /* ag number of ap->firstblock */
+       xfs_agnumber_t          ag;
+       xfs_fileoff_t           orig_offset;
+       xfs_extlen_t            orig_length;
+       xfs_extlen_t            blen;
+       xfs_extlen_t            nextminlen = 0;
+       int                     nullfb; /* true if ap->firstblock isn't set */
+       int                     isaligned;
+       int                     tryagain;
+       int                     error;
+       int                     stripe_align;
+
+       ASSERT(ap->length);
+       orig_offset = ap->offset;
+       orig_length = ap->length;
+
+       stripe_align = xfs_bmap_compute_alignments(ap, &args);
 
        nullfb = ap->tp->t_firstblock == NULLFSBLOCK;
        fb_agno = nullfb ? NULLAGNUMBER : XFS_FSB_TO_AGNO(mp,
@@ -3538,9 +3670,6 @@ xfs_bmap_btalloc(
         * Normal allocation, done through xfs_alloc_vextent.
         */
        tryagain = isaligned = 0;
-       memset(&args, 0, sizeof(args));
-       args.tp = ap->tp;
-       args.mp = mp;
        args.fsbno = ap->blkno;
        args.oinfo = XFS_RMAP_OINFO_SKIP_UPDATE;
 
@@ -3571,21 +3700,7 @@ xfs_bmap_btalloc(
                args.total = ap->total;
                args.minlen = ap->minlen;
        }
-       /* apply extent size hints if obtained earlier */
-       if (align) {
-               args.prod = align;
-               div_u64_rem(ap->offset, args.prod, &args.mod);
-               if (args.mod)
-                       args.mod = args.prod - args.mod;
-       } else if (mp->m_sb.sb_blocksize >= PAGE_SIZE) {
-               args.prod = 1;
-               args.mod = 0;
-       } else {
-               args.prod = PAGE_SIZE >> mp->m_sb.sb_blocklog;
-               div_u64_rem(ap->offset, args.prod, &args.mod);
-               if (args.mod)
-                       args.mod = args.prod - args.mod;
-       }
+
        /*
         * If we are not low on available data blocks, and the underlying
         * logical volume manager is a stripe, and the file offset is zero then
@@ -3687,37 +3802,10 @@ xfs_bmap_btalloc(
                        return error;
                ap->tp->t_flags |= XFS_TRANS_LOWMODE;
        }
+
        if (args.fsbno != NULLFSBLOCK) {
-               /*
-                * check the allocation happened at the same or higher AG than
-                * the first block that was allocated.
-                */
-               ASSERT(ap->tp->t_firstblock == NULLFSBLOCK ||
-                      XFS_FSB_TO_AGNO(mp, ap->tp->t_firstblock) <=
-                      XFS_FSB_TO_AGNO(mp, args.fsbno));
-
-               ap->blkno = args.fsbno;
-               if (ap->tp->t_firstblock == NULLFSBLOCK)
-                       ap->tp->t_firstblock = args.fsbno;
-               ASSERT(nullfb || fb_agno <= args.agno);
-               ap->length = args.len;
-               /*
-                * If the extent size hint is active, we tried to round the
-                * caller's allocation request offset down to extsz and the
-                * length up to another extsz boundary.  If we found a free
-                * extent we mapped it in starting at this new offset.  If the
-                * newly mapped space isn't long enough to cover any of the
-                * range of offsets that was originally requested, move the
-                * mapping up so that we can fill as much of the caller's
-                * original request as possible.  Free space is apparently
-                * very fragmented so we're unlikely to be able to satisfy the
-                * hints anyway.
-                */
-               if (ap->length <= orig_length)
-                       ap->offset = orig_offset;
-               else if (ap->offset + ap->length < orig_offset + orig_length)
-                       ap->offset = orig_offset + orig_length - ap->length;
-               xfs_bmap_btalloc_accounting(ap, &args);
+               xfs_bmap_process_allocated_extent(ap, &args, orig_offset,
+                       orig_length);
        } else {
                ap->blkno = NULLFSBLOCK;
                ap->length = 0;
@@ -4001,8 +4089,7 @@ xfs_bmapi_reserve_delalloc(
         * blocks.  This number gets adjusted later.  We return if we haven't
         * allocated blocks already inside this loop.
         */
-       error = xfs_trans_reserve_quota_nblks(NULL, ip, (long)alen, 0,
-                                               XFS_QMOPT_RES_REGBLKS);
+       error = xfs_quota_reserve_blkres(ip, alen);
        if (error)
                return error;
 
@@ -4048,8 +4135,7 @@ out_unreserve_blocks:
        xfs_mod_fdblocks(mp, alen, false);
 out_unreserve_quota:
        if (XFS_IS_QUOTA_ON(mp))
-               xfs_trans_unreserve_quota_nblks(NULL, ip, (long)alen, 0,
-                                               XFS_QMOPT_RES_REGBLKS);
+               xfs_quota_unreserve_blkres(ip, alen);
        return error;
 }
 
@@ -4083,6 +4169,10 @@ xfs_bmap_alloc_userdata(
                        return xfs_bmap_rtalloc(bma);
        }
 
+       if (unlikely(XFS_TEST_ERROR(false, mp,
+                       XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT)))
+               return xfs_bmap_exact_minlen_extent_alloc(bma);
+
        return xfs_bmap_btalloc(bma);
 }
 
@@ -4119,10 +4209,15 @@ xfs_bmapi_allocate(
        else
                bma->minlen = 1;
 
-       if (bma->flags & XFS_BMAPI_METADATA)
-               error = xfs_bmap_btalloc(bma);
-       else
+       if (bma->flags & XFS_BMAPI_METADATA) {
+               if (unlikely(XFS_TEST_ERROR(false, mp,
+                               XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT)))
+                       error = xfs_bmap_exact_minlen_extent_alloc(bma);
+               else
+                       error = xfs_bmap_btalloc(bma);
+       } else {
                error = xfs_bmap_alloc_userdata(bma);
+       }
        if (error || bma->blkno == NULLFSBLOCK)
                return error;
 
@@ -4527,6 +4622,12 @@ xfs_bmapi_convert_delalloc(
                return error;
 
        xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+       error = xfs_iext_count_may_overflow(ip, whichfork,
+                       XFS_IEXT_ADD_NOSPLIT_CNT);
+       if (error)
+               goto out_trans_cancel;
+
        xfs_trans_ijoin(tp, ip, 0);
 
        if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &bma.icur, &bma.got) ||
@@ -4826,9 +4927,8 @@ xfs_bmap_del_extent_delay(
         * sb counters as we might have to borrow some blocks for the
         * indirect block accounting.
         */
-       error = xfs_trans_reserve_quota_nblks(NULL, ip,
-                       -((long)del->br_blockcount), 0,
-                       isrt ? XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS);
+       ASSERT(!isrt);
+       error = xfs_quota_unreserve_blkres(ip, del->br_blockcount);
        if (error)
                return error;
        ip->i_delayed_blks -= del->br_blockcount;
@@ -5145,6 +5245,27 @@ xfs_bmap_del_extent_real(
                /*
                 * Deleting the middle of the extent.
                 */
+
+               /*
+                * For directories, -ENOSPC is returned since a directory entry
+                * remove operation must not fail due to low extent count
+                * availability. -ENOSPC will be handled by higher layers of XFS
+                * by letting the corresponding empty Data/Free blocks to linger
+                * until a future remove operation. Dabtree blocks would be
+                * swapped with the last block in the leaf space and then the
+                * new last block will be unmapped.
+                *
+                * The above logic also applies to the source directory entry of
+                * a rename operation.
+                */
+               error = xfs_iext_count_may_overflow(ip, whichfork, 1);
+               if (error) {
+                       ASSERT(S_ISDIR(VFS_I(ip)->i_mode) &&
+                               whichfork == XFS_DATA_FORK);
+                       error = -ENOSPC;
+                       goto done;
+               }
+
                old = got;
 
                got.br_blockcount = del->br_startoff - got.br_startoff;