xfs_file_last_byte() needs to acquire ilock
[safe/jmp/linux-2.6] / fs / xfs / xfs_ialloc.c
index 86d463e..3120a3a 100644 (file)
@@ -230,7 +230,7 @@ xfs_ialloc_ag_alloc(
                args.minalignslop = xfs_ialloc_cluster_alignment(&args) - 1;
 
                /* Allow space for the inode btree to split. */
-               args.minleft = XFS_IN_MAXLEVELS(args.mp) - 1;
+               args.minleft = args.mp->m_in_maxlevels - 1;
                if ((error = xfs_alloc_vextent(&args)))
                        return error;
        } else
@@ -270,7 +270,7 @@ xfs_ialloc_ag_alloc(
                /*
                 * Allow space for the inode btree to split.
                 */
-               args.minleft = XFS_IN_MAXLEVELS(args.mp) - 1;
+               args.minleft = args.mp->m_in_maxlevels - 1;
                if ((error = xfs_alloc_vextent(&args)))
                        return error;
        }
@@ -321,9 +321,9 @@ xfs_ialloc_ag_alloc(
         * able to use the file system.
         */
        if (xfs_sb_version_hasnlink(&args.mp->m_sb))
-               version = XFS_DINODE_VERSION_2;
+               version = 2;
        else
-               version = XFS_DINODE_VERSION_1;
+               version = 1;
 
        /*
         * Seed the new inode cluster with a random generation number. This
@@ -349,18 +349,18 @@ xfs_ialloc_ag_alloc(
                 * Initialize all inodes in this buffer and then log them.
                 *
                 * XXX: It would be much better if we had just one transaction to
-                *      log a whole cluster of inodes instead of all the indivdual
+                *      log a whole cluster of inodes instead of all the individual
                 *      transactions causing a lot of log traffic.
                 */
                xfs_biozero(fbuf, 0, ninodes << args.mp->m_sb.sb_inodelog);
                for (i = 0; i < ninodes; i++) {
                        int     ioffset = i << args.mp->m_sb.sb_inodelog;
-                       uint    isize = sizeof(xfs_dinode_t) + sizeof(__be32);
+                       uint    isize = sizeof(struct xfs_dinode);
 
-                       free = XFS_MAKE_IPTR(args.mp, fbuf, i);
-                       free->di_core.di_magic = cpu_to_be16(XFS_DINODE_MAGIC);
-                       free->di_core.di_version = version;
-                       free->di_core.di_gen = cpu_to_be32(gen);
+                       free = xfs_make_iptr(args.mp, fbuf, i);
+                       free->di_magic = cpu_to_be16(XFS_DINODE_MAGIC);
+                       free->di_version = version;
+                       free->di_gen = cpu_to_be32(gen);
                        free->di_next_unlinked = cpu_to_be32(NULLAGINO);
                        xfs_trans_log_buf(tp, fbuf, ioffset, ioffset + isize - 1);
                }
@@ -937,13 +937,13 @@ nextag:
                        }
                }
        }
-       offset = XFS_IALLOC_FIND_FREE(&rec.ir_free);
+       offset = xfs_ialloc_find_free(&rec.ir_free);
        ASSERT(offset >= 0);
        ASSERT(offset < XFS_INODES_PER_CHUNK);
        ASSERT((XFS_AGINO_TO_OFFSET(mp, rec.ir_startino) %
                                   XFS_INODES_PER_CHUNK) == 0);
        ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino + offset);
-       XFS_INOBT_CLR_FREE(&rec, offset);
+       rec.ir_free &= ~XFS_INOBT_MASK(offset);
        rec.ir_freecount--;
        if ((error = xfs_inobt_update(cur, rec.ir_startino, rec.ir_freecount,
                        rec.ir_free)))
@@ -1105,11 +1105,11 @@ xfs_difree(
         */
        off = agino - rec.ir_startino;
        ASSERT(off >= 0 && off < XFS_INODES_PER_CHUNK);
-       ASSERT(!XFS_INOBT_IS_FREE(&rec, off));
+       ASSERT(!(rec.ir_free & XFS_INOBT_MASK(off)));
        /*
         * Mark the inode free & increment the count.
         */
-       XFS_INOBT_SET_FREE(&rec, off);
+       rec.ir_free |= XFS_INOBT_MASK(off);
        rec.ir_freecount++;
 
        /*
@@ -1196,36 +1196,28 @@ error0:
 }
 
 /*
- * Return the location of the inode in bno/off, for mapping it into a buffer.
+ * Return the location of the inode in imap, for mapping it into a buffer.
  */
-/*ARGSUSED*/
 int
-xfs_dilocate(
-       xfs_mount_t     *mp,    /* file system mount structure */
-       xfs_trans_t     *tp,    /* transaction pointer */
+xfs_imap(
+       xfs_mount_t      *mp,   /* file system mount structure */
+       xfs_trans_t      *tp,   /* transaction pointer */
        xfs_ino_t       ino,    /* inode to locate */
-       xfs_fsblock_t   *bno,   /* output: block containing inode */
-       int             *len,   /* output: num blocks in inode cluster */
-       int             *off,   /* output: index in block of inode */
-       uint            flags)  /* flags concerning inode lookup */
+       struct xfs_imap *imap,  /* location map structure */
+       uint            flags)  /* flags for inode btree lookup */
 {
        xfs_agblock_t   agbno;  /* block number of inode in the alloc group */
-       xfs_buf_t       *agbp;  /* agi buffer */
        xfs_agino_t     agino;  /* inode number within alloc group */
        xfs_agnumber_t  agno;   /* allocation group number */
        int             blks_per_cluster; /* num blocks per inode cluster */
        xfs_agblock_t   chunk_agbno;    /* first block in inode chunk */
-       xfs_agino_t     chunk_agino;    /* first agino in inode chunk */
-       __int32_t       chunk_cnt;      /* count of free inodes in chunk */
-       xfs_inofree_t   chunk_free;     /* mask of free inodes in chunk */
        xfs_agblock_t   cluster_agbno;  /* first block in inode cluster */
-       xfs_btree_cur_t *cur;   /* inode btree cursor */
        int             error;  /* error code */
-       int             i;      /* temp state */
        int             offset; /* index of inode in its buffer */
        int             offset_agbno;   /* blks from chunk start to inode */
 
        ASSERT(ino != NULLFSINO);
+
        /*
         * Split up the inode number into its parts.
         */
@@ -1236,24 +1228,24 @@ xfs_dilocate(
            ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
 #ifdef DEBUG
                /* no diagnostics for bulkstat, ino comes from userspace */
-               if (flags & XFS_IMAP_BULKSTAT)
+               if (flags & XFS_IGET_BULKSTAT)
                        return XFS_ERROR(EINVAL);
                if (agno >= mp->m_sb.sb_agcount) {
                        xfs_fs_cmn_err(CE_ALERT, mp,
-                                       "xfs_dilocate: agno (%d) >= "
+                                       "xfs_imap: agno (%d) >= "
                                        "mp->m_sb.sb_agcount (%d)",
                                        agno,  mp->m_sb.sb_agcount);
                }
                if (agbno >= mp->m_sb.sb_agblocks) {
                        xfs_fs_cmn_err(CE_ALERT, mp,
-                                       "xfs_dilocate: agbno (0x%llx) >= "
+                                       "xfs_imap: agbno (0x%llx) >= "
                                        "mp->m_sb.sb_agblocks (0x%lx)",
                                        (unsigned long long) agbno,
                                        (unsigned long) mp->m_sb.sb_agblocks);
                }
                if (ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
                        xfs_fs_cmn_err(CE_ALERT, mp,
-                                       "xfs_dilocate: ino (0x%llx) != "
+                                       "xfs_imap: ino (0x%llx) != "
                                        "XFS_AGINO_TO_INO(mp, agno, agino) "
                                        "(0x%llx)",
                                        ino, XFS_AGINO_TO_INO(mp, agno, agino));
@@ -1262,64 +1254,89 @@ xfs_dilocate(
 #endif /* DEBUG */
                return XFS_ERROR(EINVAL);
        }
-       if ((mp->m_sb.sb_blocksize >= XFS_INODE_CLUSTER_SIZE(mp)) ||
-           !(flags & XFS_IMAP_LOOKUP)) {
+
+       /*
+        * If the inode cluster size is the same as the blocksize or
+        * smaller we get to the buffer by simple arithmetics.
+        */
+       if (XFS_INODE_CLUSTER_SIZE(mp) <= mp->m_sb.sb_blocksize) {
                offset = XFS_INO_TO_OFFSET(mp, ino);
                ASSERT(offset < mp->m_sb.sb_inopblock);
-               *bno = XFS_AGB_TO_FSB(mp, agno, agbno);
-               *off = offset;
-               *len = 1;
+
+               imap->im_blkno = XFS_AGB_TO_DADDR(mp, agno, agbno);
+               imap->im_len = XFS_FSB_TO_BB(mp, 1);
+               imap->im_boffset = (ushort)(offset << mp->m_sb.sb_inodelog);
                return 0;
        }
+
        blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog;
-       if (*bno != NULLFSBLOCK) {
+
+       /*
+        * If we get a block number passed from bulkstat we can use it to
+        * find the buffer easily.
+        */
+       if (imap->im_blkno) {
                offset = XFS_INO_TO_OFFSET(mp, ino);
                ASSERT(offset < mp->m_sb.sb_inopblock);
-               cluster_agbno = XFS_FSB_TO_AGBNO(mp, *bno);
-               *off = ((agbno - cluster_agbno) * mp->m_sb.sb_inopblock) +
-                       offset;
-               *len = blks_per_cluster;
+
+               cluster_agbno = xfs_daddr_to_agbno(mp, imap->im_blkno);
+               offset += (agbno - cluster_agbno) * mp->m_sb.sb_inopblock;
+
+               imap->im_len = XFS_FSB_TO_BB(mp, blks_per_cluster);
+               imap->im_boffset = (ushort)(offset << mp->m_sb.sb_inodelog);
                return 0;
        }
+
+       /*
+        * If the inode chunks are aligned then use simple maths to
+        * find the location. Otherwise we have to do a btree
+        * lookup to find the location.
+        */
        if (mp->m_inoalign_mask) {
                offset_agbno = agbno & mp->m_inoalign_mask;
                chunk_agbno = agbno - offset_agbno;
        } else {
+               xfs_btree_cur_t *cur;   /* inode btree cursor */
+               xfs_agino_t     chunk_agino; /* first agino in inode chunk */
+               __int32_t       chunk_cnt; /* count of free inodes in chunk */
+               xfs_inofree_t   chunk_free; /* mask of free inodes in chunk */
+               xfs_buf_t       *agbp;  /* agi buffer */
+               int             i;      /* temp state */
+
                down_read(&mp->m_peraglock);
                error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
                up_read(&mp->m_peraglock);
                if (error) {
-#ifdef DEBUG
-                       xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "
+                       xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
                                        "xfs_ialloc_read_agi() returned "
                                        "error %d, agno %d",
                                        error, agno);
-#endif /* DEBUG */
                        return error;
                }
+
                cur = xfs_inobt_init_cursor(mp, tp, agbp, agno);
-               if ((error = xfs_inobt_lookup_le(cur, agino, 0, 0, &i))) {
-#ifdef DEBUG
-                       xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "
+               error = xfs_inobt_lookup_le(cur, agino, 0, 0, &i);
+               if (error) {
+                       xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
                                        "xfs_inobt_lookup_le() failed");
-#endif /* DEBUG */
                        goto error0;
                }
-               if ((error = xfs_inobt_get_rec(cur, &chunk_agino, &chunk_cnt,
-                               &chunk_free, &i))) {
-#ifdef DEBUG
-                       xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "
+
+               error = xfs_inobt_get_rec(cur, &chunk_agino, &chunk_cnt,
+                               &chunk_free, &i);
+               if (error) {
+                       xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
                                        "xfs_inobt_get_rec() failed");
-#endif /* DEBUG */
                        goto error0;
                }
                if (i == 0) {
 #ifdef DEBUG
-                       xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "
+                       xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
                                        "xfs_inobt_get_rec() failed");
 #endif /* DEBUG */
                        error = XFS_ERROR(EINVAL);
                }
+ error0:
                xfs_trans_brelse(tp, agbp);
                xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
                if (error)
@@ -1327,19 +1344,35 @@ xfs_dilocate(
                chunk_agbno = XFS_AGINO_TO_AGBNO(mp, chunk_agino);
                offset_agbno = agbno - chunk_agbno;
        }
+
        ASSERT(agbno >= chunk_agbno);
        cluster_agbno = chunk_agbno +
                ((offset_agbno / blks_per_cluster) * blks_per_cluster);
        offset = ((agbno - cluster_agbno) * mp->m_sb.sb_inopblock) +
                XFS_INO_TO_OFFSET(mp, ino);
-       *bno = XFS_AGB_TO_FSB(mp, agno, cluster_agbno);
-       *off = offset;
-       *len = blks_per_cluster;
+
+       imap->im_blkno = XFS_AGB_TO_DADDR(mp, agno, cluster_agbno);
+       imap->im_len = XFS_FSB_TO_BB(mp, blks_per_cluster);
+       imap->im_boffset = (ushort)(offset << mp->m_sb.sb_inodelog);
+
+       /*
+        * If the inode number maps to a block outside the bounds
+        * of the file system then return NULL rather than calling
+        * read_buf and panicing when we get an error from the
+        * driver.
+        */
+       if ((imap->im_blkno + imap->im_len) >
+           XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)) {
+               xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
+                       "(imap->im_blkno (0x%llx) + imap->im_len (0x%llx)) > "
+                       " XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks) (0x%llx)",
+                       (unsigned long long) imap->im_blkno,
+                       (unsigned long long) imap->im_len,
+                       XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks));
+               return XFS_ERROR(EINVAL);
+       }
+
        return 0;
-error0:
-       xfs_trans_brelse(tp, agbp);
-       xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
-       return error;
 }
 
 /*