[XFS] Concurrent Multi-File Data Streams
[linux-2.6-block.git] / fs / xfs / xfs_bmap.c
index 50f2213589f886758e097393d651a22940f6e69e..51ba689a45526d51709fb9d36846c4adc8cbfee0 100644 (file)
@@ -52,6 +52,7 @@
 #include "xfs_quota.h"
 #include "xfs_trans_space.h"
 #include "xfs_buf_item.h"
+#include "xfs_filestream.h"
 
 
 #ifdef DEBUG
@@ -2605,12 +2606,10 @@ xfs_bmap_rtalloc(
        xfs_extlen_t    prod = 0;       /* product factor for allocators */
        xfs_extlen_t    ralen = 0;      /* realtime allocation length */
        xfs_extlen_t    align;          /* minimum allocation alignment */
-       xfs_rtblock_t   rtx;            /* realtime extent number */
        xfs_rtblock_t   rtb;
 
        mp = ap->ip->i_mount;
-       align = ap->ip->i_d.di_extsize ?
-               ap->ip->i_d.di_extsize : mp->m_sb.sb_rextsize;
+       align = xfs_get_extsz_hint(ap->ip);
        prod = align / mp->m_sb.sb_rextsize;
        error = xfs_bmap_extsize_align(mp, ap->gotp, ap->prevp,
                                        align, 1, ap->eof, 0,
@@ -2644,6 +2643,8 @@ xfs_bmap_rtalloc(
         * pick an extent that will space things out in the rt area.
         */
        if (ap->eof && ap->off == 0) {
+               xfs_rtblock_t uninitialized_var(rtx); /* realtime extent no */
+
                error = xfs_rtpick_extent(mp, ap->tp, ralen, &rtx);
                if (error)
                        return error;
@@ -2715,9 +2716,7 @@ xfs_bmap_btalloc(
        int             error;
 
        mp = ap->ip->i_mount;
-       align = (ap->userdata && ap->ip->i_d.di_extsize &&
-               (ap->ip->i_d.di_flags & XFS_DIFLAG_EXTSIZE)) ?
-               ap->ip->i_d.di_extsize : 0;
+       align = ap->userdata ? xfs_get_extsz_hint(ap->ip) : 0;
        if (unlikely(align)) {
                error = xfs_bmap_extsize_align(mp, ap->gotp, ap->prevp,
                                                align, 0, ap->eof, 0, ap->conv,
@@ -2727,9 +2726,15 @@ xfs_bmap_btalloc(
        }
        nullfb = ap->firstblock == NULLFSBLOCK;
        fb_agno = nullfb ? NULLAGNUMBER : XFS_FSB_TO_AGNO(mp, ap->firstblock);
-       if (nullfb)
-               ap->rval = XFS_INO_TO_FSB(mp, ap->ip->i_ino);
-       else
+       if (nullfb) {
+               if (ap->userdata && xfs_inode_is_filestream(ap->ip)) {
+                       ag = xfs_filestream_lookup_ag(ap->ip);
+                       ag = (ag != NULLAGNUMBER) ? ag : 0;
+                       ap->rval = XFS_AGB_TO_FSB(mp, ag, 0);
+               } else {
+                       ap->rval = XFS_INO_TO_FSB(mp, ap->ip->i_ino);
+               }
+       } else
                ap->rval = ap->firstblock;
 
        xfs_bmap_adjacent(ap);
@@ -2753,13 +2758,22 @@ xfs_bmap_btalloc(
        args.firstblock = ap->firstblock;
        blen = 0;
        if (nullfb) {
-               args.type = XFS_ALLOCTYPE_START_BNO;
+               if (ap->userdata && xfs_inode_is_filestream(ap->ip))
+                       args.type = XFS_ALLOCTYPE_NEAR_BNO;
+               else
+                       args.type = XFS_ALLOCTYPE_START_BNO;
                args.total = ap->total;
+
                /*
-                * Find the longest available space.
-                * We're going to try for the whole allocation at once.
+                * Search for an allocation group with a single extent
+                * large enough for the request.
+                *
+                * If one isn't found, then adjust the minimum allocation
+                * size to the largest space found.
                 */
                startag = ag = XFS_FSB_TO_AGNO(mp, args.fsbno);
+               if (startag == NULLAGNUMBER)
+                       startag = ag = 0;
                notinit = 0;
                down_read(&mp->m_peraglock);
                while (blen < ap->alen) {
@@ -2785,6 +2799,35 @@ xfs_bmap_btalloc(
                                        blen = longest;
                        } else
                                notinit = 1;
+
+                       if (xfs_inode_is_filestream(ap->ip)) {
+                               if (blen >= ap->alen)
+                                       break;
+
+                               if (ap->userdata) {
+                                       /*
+                                        * If startag is an invalid AG, we've
+                                        * come here once before and
+                                        * xfs_filestream_new_ag picked the
+                                        * best currently available.
+                                        *
+                                        * Don't continue looping, since we
+                                        * could loop forever.
+                                        */
+                                       if (startag == NULLAGNUMBER)
+                                               break;
+
+                                       error = xfs_filestream_new_ag(ap, &ag);
+                                       if (error) {
+                                               up_read(&mp->m_peraglock);
+                                               return error;
+                                       }
+
+                                       /* loop again to set 'blen'*/
+                                       startag = NULLAGNUMBER;
+                                       continue;
+                               }
+                       }
                        if (++ag == mp->m_sb.sb_agcount)
                                ag = 0;
                        if (ag == startag)
@@ -2809,17 +2852,27 @@ xfs_bmap_btalloc(
                 */
                else
                        args.minlen = ap->alen;
+
+               /*
+                * set the failure fallback case to look in the selected
+                * AG as the stream may have moved.
+                */
+               if (xfs_inode_is_filestream(ap->ip))
+                       ap->rval = args.fsbno = XFS_AGB_TO_FSB(mp, ag, 0);
        } else if (ap->low) {
-               args.type = XFS_ALLOCTYPE_START_BNO;
+               if (xfs_inode_is_filestream(ap->ip))
+                       args.type = XFS_ALLOCTYPE_FIRST_AG;
+               else
+                       args.type = XFS_ALLOCTYPE_START_BNO;
                args.total = args.minlen = ap->minlen;
        } else {
                args.type = XFS_ALLOCTYPE_NEAR_BNO;
                args.total = ap->total;
                args.minlen = ap->minlen;
        }
-       if (unlikely(ap->userdata && ap->ip->i_d.di_extsize &&
-                   (ap->ip->i_d.di_flags & XFS_DIFLAG_EXTSIZE))) {
-               args.prod = ap->ip->i_d.di_extsize;
+       /* apply extent size hints if obtained earlier */
+       if (unlikely(align)) {
+               args.prod = align;
                if ((args.mod = (xfs_extlen_t)do_mod(ap->off, args.prod)))
                        args.mod = (xfs_extlen_t)(args.prod - args.mod);
        } else if (mp->m_sb.sb_blocksize >= NBPP) {
@@ -4444,8 +4497,11 @@ xfs_bmap_one_block(
        xfs_bmbt_irec_t s;              /* internal version of extent */
 
 #ifndef DEBUG
-       if (whichfork == XFS_DATA_FORK)
-               return ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize;
+       if (whichfork == XFS_DATA_FORK) {
+               return ((ip->i_d.di_mode & S_IFMT) == S_IFREG) ?
+                       (ip->i_size == ip->i_mount->m_sb.sb_blocksize) :
+                       (ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize);
+       }
 #endif /* !DEBUG */
        if (XFS_IFORK_NEXTENTS(ip, whichfork) != 1)
                return 0;
@@ -4457,7 +4513,7 @@ xfs_bmap_one_block(
        xfs_bmbt_get_all(ep, &s);
        rval = s.br_startoff == 0 && s.br_blockcount == 1;
        if (rval && whichfork == XFS_DATA_FORK)
-               ASSERT(ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize);
+               ASSERT(ip->i_size == ip->i_mount->m_sb.sb_blocksize);
        return rval;
 }
 
@@ -4865,12 +4921,7 @@ xfs_bmapi(
                                xfs_extlen_t    extsz;
 
                                /* Figure out the extent size, adjust alen */
-                               if (rt) {
-                                       if (!(extsz = ip->i_d.di_extsize))
-                                               extsz = mp->m_sb.sb_rextsize;
-                               } else {
-                                       extsz = ip->i_d.di_extsize;
-                               }
+                               extsz = xfs_get_extsz_hint(ip);
                                if (extsz) {
                                        error = xfs_bmap_extsize_align(mp,
                                                        &got, &prev, extsz,
@@ -5216,10 +5267,10 @@ xfs_bmapi(
                 * Else go on to the next record.
                 */
                ep = xfs_iext_get_ext(ifp, ++lastx);
-               if (lastx >= nextents) {
+               prev = got;
+               if (lastx >= nextents)
                        eof = 1;
-                       prev = got;
-               } else
+               else
                        xfs_bmbt_get_all(ep, &got);
        }
        ifp->if_lastex = lastx;
@@ -5810,14 +5861,13 @@ xfs_getbmap(
                   ip->i_d.di_format != XFS_DINODE_FMT_LOCAL)
                return XFS_ERROR(EINVAL);
        if (whichfork == XFS_DATA_FORK) {
-               if ((ip->i_d.di_extsize && (ip->i_d.di_flags &
-                               (XFS_DIFLAG_REALTIME|XFS_DIFLAG_EXTSIZE))) ||
+               if (xfs_get_extsz_hint(ip) ||
                    ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC|XFS_DIFLAG_APPEND)){
                        prealloced = 1;
                        fixlen = XFS_MAXIOFFSET(mp);
                } else {
                        prealloced = 0;
-                       fixlen = ip->i_d.di_size;
+                       fixlen = ip->i_size;
                }
        } else {
                prealloced = 0;
@@ -5841,7 +5891,8 @@ xfs_getbmap(
 
        xfs_ilock(ip, XFS_IOLOCK_SHARED);
 
-       if (whichfork == XFS_DATA_FORK && ip->i_delayed_blks) {
+       if (whichfork == XFS_DATA_FORK &&
+               (ip->i_delayed_blks || ip->i_size > ip->i_d.di_size)) {
                /* xfs_fsize_t last_byte = xfs_file_last_byte(ip); */
                error = bhv_vop_flush_pages(vp, (xfs_off_t)0, -1, 0, FI_REMAPF);
        }