]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/xfs/xfs_inode.c
[XFS] sanitize xfs_initialize_vnode
[linux-2.6-omap-h63xx.git] / fs / xfs / xfs_inode.c
index ca074ee01d067ec3ac8ea7cc67440d060b419c00..5bb638b445e8cfba6364d970a74613b92297c569 100644 (file)
@@ -1046,7 +1046,6 @@ xfs_ialloc(
 {
        xfs_ino_t       ino;
        xfs_inode_t     *ip;
-       bhv_vnode_t     *vp;
        uint            flags;
        int             error;
 
@@ -1077,7 +1076,6 @@ xfs_ialloc(
        }
        ASSERT(ip != NULL);
 
-       vp = XFS_ITOV(ip);
        ip->i_d.di_mode = (__uint16_t)mode;
        ip->i_d.di_onlink = 0;
        ip->i_d.di_nlink = nlink;
@@ -1220,7 +1218,7 @@ xfs_ialloc(
        xfs_trans_log_inode(tp, ip, flags);
 
        /* now that we have an i_mode we can setup inode ops and unlock */
-       xfs_initialize_vnode(tp->t_mountp, vp, ip);
+       xfs_setup_inode(ip);
 
        *ipp = ip;
        return 0;
@@ -1291,7 +1289,7 @@ xfs_file_last_byte(
        xfs_fileoff_t   size_last_block;
        int             error;
 
-       ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE | MR_ACCESS));
+       ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL|XFS_IOLOCK_SHARED));
 
        mp = ip->i_mount;
        /*
@@ -1399,16 +1397,14 @@ xfs_itruncate_start(
        xfs_fsize_t     last_byte;
        xfs_off_t       toss_start;
        xfs_mount_t     *mp;
-       bhv_vnode_t     *vp;
        int             error = 0;
 
-       ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE) != 0);
+       ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
        ASSERT((new_size == 0) || (new_size <= ip->i_size));
        ASSERT((flags == XFS_ITRUNC_DEFINITE) ||
               (flags == XFS_ITRUNC_MAYBE));
 
        mp = ip->i_mount;
-       vp = XFS_ITOV(ip);
 
        /* wait for the completion of any pending DIOs */
        if (new_size < ip->i_size)
@@ -1457,58 +1453,57 @@ xfs_itruncate_start(
 
 #ifdef DEBUG
        if (new_size == 0) {
-               ASSERT(VN_CACHED(vp) == 0);
+               ASSERT(VN_CACHED(VFS_I(ip)) == 0);
        }
 #endif
        return error;
 }
 
 /*
- * Shrink the file to the given new_size.  The new
- * size must be smaller than the current size.
- * This will free up the underlying blocks
- * in the removed range after a call to xfs_itruncate_start()
- * or xfs_atruncate_start().
+ * Shrink the file to the given new_size.  The new size must be smaller than
+ * the current size.  This will free up the underlying blocks in the removed
+ * range after a call to xfs_itruncate_start() or xfs_atruncate_start().
  *
- * The transaction passed to this routine must have made
- * a permanent log reservation of at least XFS_ITRUNCATE_LOG_RES.
- * This routine may commit the given transaction and
- * start new ones, so make sure everything involved in
- * the transaction is tidy before calling here.
- * Some transaction will be returned to the caller to be
- * committed.  The incoming transaction must already include
- * the inode, and both inode locks must be held exclusively.
- * The inode must also be "held" within the transaction.  On
- * return the inode will be "held" within the returned transaction.
- * This routine does NOT require any disk space to be reserved
- * for it within the transaction.
+ * The transaction passed to this routine must have made a permanent log
+ * reservation of at least XFS_ITRUNCATE_LOG_RES.  This routine may commit the
+ * given transaction and start new ones, so make sure everything involved in
+ * the transaction is tidy before calling here.  Some transaction will be
+ * returned to the caller to be committed.  The incoming transaction must
+ * already include the inode, and both inode locks must be held exclusively.
+ * The inode must also be "held" within the transaction.  On return the inode
+ * will be "held" within the returned transaction.  This routine does NOT
+ * require any disk space to be reserved for it within the transaction.
  *
- * The fork parameter must be either xfs_attr_fork or xfs_data_fork,
- * and it indicates the fork which is to be truncated.  For the
- * attribute fork we only support truncation to size 0.
+ * The fork parameter must be either xfs_attr_fork or xfs_data_fork, and it
+ * indicates the fork which is to be truncated.  For the attribute fork we only
+ * support truncation to size 0.
  *
- * We use the sync parameter to indicate whether or not the first
- * transaction we perform might have to be synchronous.  For the attr fork,
- * it needs to be so if the unlink of the inode is not yet known to be
- * permanent in the log.  This keeps us from freeing and reusing the
- * blocks of the attribute fork before the unlink of the inode becomes
- * permanent.
+ * We use the sync parameter to indicate whether or not the first transaction
+ * we perform might have to be synchronous.  For the attr fork, it needs to be
+ * so if the unlink of the inode is not yet known to be permanent in the log.
+ * This keeps us from freeing and reusing the blocks of the attribute fork
+ * before the unlink of the inode becomes permanent.
  *
- * For the data fork, we normally have to run synchronously if we're
- * being called out of the inactive path or we're being called
- * out of the create path where we're truncating an existing file.
- * Either way, the truncate needs to be sync so blocks don't reappear
- * in the file with altered data in case of a crash.  wsync filesystems
- * can run the first case async because anything that shrinks the inode
- * has to run sync so by the time we're called here from inactive, the
- * inode size is permanently set to 0.
+ * For the data fork, we normally have to run synchronously if we're being
+ * called out of the inactive path or we're being called out of the create path
+ * where we're truncating an existing file.  Either way, the truncate needs to
+ * be sync so blocks don't reappear in the file with altered data in case of a
+ * crash.  wsync filesystems can run the first case async because anything that
+ * shrinks the inode has to run sync so by the time we're called here from
+ * inactive, the inode size is permanently set to 0.
  *
- * Calls from the truncate path always need to be sync unless we're
- * in a wsync filesystem and the file has already been unlinked.
+ * Calls from the truncate path always need to be sync unless we're in a wsync
+ * filesystem and the file has already been unlinked.
  *
- * The caller is responsible for correctly setting the sync parameter.
- * It gets too hard for us to guess here which path we're being called
- * out of just based on inode state.
+ * The caller is responsible for correctly setting the sync parameter.  It gets
+ * too hard for us to guess here which path we're being called out of just
+ * based on inode state.
+ *
+ * If we get an error, we must return with the inode locked and linked into the
+ * current transaction. This keeps things simple for the higher level code,
+ * because it always knows that the inode is locked and held in the transaction
+ * that returns to it whether errors occur or not.  We don't mark the inode
+ * dirty on error so that transactions can be easily aborted if possible.
  */
 int
 xfs_itruncate_finish(
@@ -1529,8 +1524,7 @@ xfs_itruncate_finish(
        xfs_bmap_free_t free_list;
        int             error;
 
-       ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE) != 0);
-       ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE) != 0);
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_IOLOCK_EXCL));
        ASSERT((new_size == 0) || (new_size <= ip->i_size));
        ASSERT(*tp != NULL);
        ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES);
@@ -1687,45 +1681,51 @@ xfs_itruncate_finish(
                 */
                error = xfs_bmap_finish(tp, &free_list, &committed);
                ntp = *tp;
+               if (committed) {
+                       /* link the inode into the next xact in the chain */
+                       xfs_trans_ijoin(ntp, ip,
+                                       XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+                       xfs_trans_ihold(ntp, ip);
+               }
+
                if (error) {
                        /*
-                        * If the bmap finish call encounters an error,
-                        * return to the caller where the transaction
-                        * can be properly aborted.  We just need to
-                        * make sure we're not holding any resources
-                        * that we were not when we came in.
+                        * If the bmap finish call encounters an error, return
+                        * to the caller where the transaction can be properly
+                        * aborted.  We just need to make sure we're not
+                        * holding any resources that we were not when we came
+                        * in.
                         *
-                        * Aborting from this point might lose some
-                        * blocks in the file system, but oh well.
+                        * Aborting from this point might lose some blocks in
+                        * the file system, but oh well.
                         */
                        xfs_bmap_cancel(&free_list);
-                       if (committed)
-                               goto error_join;
                        return error;
                }
 
                if (committed) {
                        /*
-                        * The first xact was committed, so add the inode to
-                        * the new one.  Mark it dirty so it will be logged and
+                        * Mark the inode dirty so it will be logged and
                         * moved forward in the log as part of every commit.
                         */
-                       xfs_trans_ijoin(ntp, ip,
-                                       XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
-                       xfs_trans_ihold(ntp, ip);
                        xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
                }
+
                ntp = xfs_trans_dup(ntp);
                error = xfs_trans_commit(*tp, 0);
                *tp = ntp;
-               if (error)
-                       goto error_join;
-               error = xfs_trans_reserve(ntp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,
-                                         XFS_TRANS_PERM_LOG_RES,
-                                         XFS_ITRUNCATE_LOG_COUNT);
-               if (error)
-                       goto error_join;
 
+               /* link the inode into the next transaction in the chain */
+               xfs_trans_ijoin(ntp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+               xfs_trans_ihold(ntp, ip);
+
+               if (!error)
+                       error = xfs_trans_reserve(ntp, 0,
+                                       XFS_ITRUNCATE_LOG_RES(mp), 0,
+                                       XFS_TRANS_PERM_LOG_RES,
+                                       XFS_ITRUNCATE_LOG_COUNT);
+               if (error)
+                       return error;
        }
        /*
         * Only update the size in the case of the data fork, but
@@ -1757,83 +1757,8 @@ xfs_itruncate_finish(
               (ip->i_d.di_nextents == 0));
        xfs_itrunc_trace(XFS_ITRUNC_FINISH2, ip, 0, new_size, 0, 0);
        return 0;
-
-error_join:
-       /*
-        * Add the inode being truncated to the next chained transaction.  This
-        * keeps things simple for the higher level code, because it always
-        * knows that the inode is locked and held in the transaction that
-        * returns to it whether errors occur or not.  We don't mark the inode
-        * dirty so that this transaction can be easily aborted if possible.
-        */
-       xfs_trans_ijoin(ntp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
-       xfs_trans_ihold(ntp, ip);
-       return error;
-}
-
-
-/*
- * xfs_igrow_start
- *
- * Do the first part of growing a file: zero any data in the last
- * block that is beyond the old EOF.  We need to do this before
- * the inode is joined to the transaction to modify the i_size.
- * That way we can drop the inode lock and call into the buffer
- * cache to get the buffer mapping the EOF.
- */
-int
-xfs_igrow_start(
-       xfs_inode_t     *ip,
-       xfs_fsize_t     new_size,
-       cred_t          *credp)
-{
-       ASSERT(ismrlocked(&(ip->i_lock), MR_UPDATE) != 0);
-       ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE) != 0);
-       ASSERT(new_size > ip->i_size);
-
-       /*
-        * Zero any pages that may have been created by
-        * xfs_write_file() beyond the end of the file
-        * and any blocks between the old and new file sizes.
-        */
-       return xfs_zero_eof(ip, new_size, ip->i_size);
 }
 
-/*
- * xfs_igrow_finish
- *
- * This routine is called to extend the size of a file.
- * The inode must have both the iolock and the ilock locked
- * for update and it must be a part of the current transaction.
- * The xfs_igrow_start() function must have been called previously.
- * If the change_flag is not zero, the inode change timestamp will
- * be updated.
- */
-void
-xfs_igrow_finish(
-       xfs_trans_t     *tp,
-       xfs_inode_t     *ip,
-       xfs_fsize_t     new_size,
-       int             change_flag)
-{
-       ASSERT(ismrlocked(&(ip->i_lock), MR_UPDATE) != 0);
-       ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE) != 0);
-       ASSERT(ip->i_transp == tp);
-       ASSERT(new_size > ip->i_size);
-
-       /*
-        * Update the file size.  Update the inode change timestamp
-        * if change_flag set.
-        */
-       ip->i_d.di_size = new_size;
-       ip->i_size = new_size;
-       if (change_flag)
-               xfs_ichgtime(ip, XFS_ICHGTIME_CHG);
-       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-
-}
-
-
 /*
  * This is called when the inode's link count goes to 0.
  * We place the on-disk inode on a list in the AGI.  It
@@ -2268,7 +2193,7 @@ xfs_ifree_cluster(
                xfs_trans_binval(tp, bp);
        }
 
-       kmem_free(ip_found, ninodes * sizeof(xfs_inode_t *));
+       kmem_free(ip_found);
        xfs_put_perag(mp, pag);
 }
 
@@ -2294,7 +2219,7 @@ xfs_ifree(
        xfs_dinode_t            *dip;
        xfs_buf_t               *ibp;
 
-       ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE));
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
        ASSERT(ip->i_transp == tp);
        ASSERT(ip->i_d.di_nlink == 0);
        ASSERT(ip->i_d.di_nextents == 0);
@@ -2480,7 +2405,7 @@ xfs_iroot_realloc(
                                                     (int)new_size);
                memcpy(np, op, new_max * (uint)sizeof(xfs_dfsbno_t));
        }
-       kmem_free(ifp->if_broot, ifp->if_broot_bytes);
+       kmem_free(ifp->if_broot);
        ifp->if_broot = new_broot;
        ifp->if_broot_bytes = (int)new_size;
        ASSERT(ifp->if_broot_bytes <=
@@ -2524,7 +2449,7 @@ xfs_idata_realloc(
 
        if (new_size == 0) {
                if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
-                       kmem_free(ifp->if_u1.if_data, ifp->if_real_bytes);
+                       kmem_free(ifp->if_u1.if_data);
                }
                ifp->if_u1.if_data = NULL;
                real_size = 0;
@@ -2539,7 +2464,7 @@ xfs_idata_realloc(
                        ASSERT(ifp->if_real_bytes != 0);
                        memcpy(ifp->if_u2.if_inline_data, ifp->if_u1.if_data,
                              new_size);
-                       kmem_free(ifp->if_u1.if_data, ifp->if_real_bytes);
+                       kmem_free(ifp->if_u1.if_data);
                        ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
                }
                real_size = 0;
@@ -2646,7 +2571,7 @@ xfs_idestroy_fork(
 
        ifp = XFS_IFORK_PTR(ip, whichfork);
        if (ifp->if_broot != NULL) {
-               kmem_free(ifp->if_broot, ifp->if_broot_bytes);
+               kmem_free(ifp->if_broot);
                ifp->if_broot = NULL;
        }
 
@@ -2660,7 +2585,7 @@ xfs_idestroy_fork(
                if ((ifp->if_u1.if_data != ifp->if_u2.if_inline_data) &&
                    (ifp->if_u1.if_data != NULL)) {
                        ASSERT(ifp->if_real_bytes != 0);
-                       kmem_free(ifp->if_u1.if_data, ifp->if_real_bytes);
+                       kmem_free(ifp->if_u1.if_data);
                        ifp->if_u1.if_data = NULL;
                        ifp->if_real_bytes = 0;
                }
@@ -2753,7 +2678,7 @@ void
 xfs_ipin(
        xfs_inode_t     *ip)
 {
-       ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE));
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
 
        atomic_inc(&ip->i_pincount);
 }
@@ -2786,7 +2711,7 @@ __xfs_iunpin_wait(
 {
        xfs_inode_log_item_t    *iip = ip->i_itemp;
 
-       ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE | MR_ACCESS));
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
        if (atomic_read(&ip->i_pincount) == 0)
                return;
 
@@ -2836,7 +2761,7 @@ xfs_iextents_copy(
        xfs_fsblock_t           start_block;
 
        ifp = XFS_IFORK_PTR(ip, whichfork);
-       ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE|MR_ACCESS));
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
        ASSERT(ifp->if_bytes > 0);
 
        nrecs = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
@@ -2883,7 +2808,7 @@ xfs_iextents_copy(
  * format indicates the current state of the fork.
  */
 /*ARGSUSED*/
-STATIC int
+STATIC void
 xfs_iflush_fork(
        xfs_inode_t             *ip,
        xfs_dinode_t            *dip,
@@ -2904,16 +2829,16 @@ xfs_iflush_fork(
        static const short      extflag[2] =
                { XFS_ILOG_DEXT, XFS_ILOG_AEXT };
 
-       if (iip == NULL)
-               return 0;
+       if (!iip)
+               return;
        ifp = XFS_IFORK_PTR(ip, whichfork);
        /*
         * This can happen if we gave up in iformat in an error path,
         * for the attribute fork.
         */
-       if (ifp == NULL) {
+       if (!ifp) {
                ASSERT(whichfork == XFS_ATTR_FORK);
-               return 0;
+               return;
        }
        cp = XFS_DFORK_PTR(dip, whichfork);
        mp = ip->i_mount;
@@ -2974,8 +2899,6 @@ xfs_iflush_fork(
                ASSERT(0);
                break;
        }
-
-       return 0;
 }
 
 STATIC int
@@ -2986,6 +2909,7 @@ xfs_iflush_cluster(
        xfs_mount_t             *mp = ip->i_mount;
        xfs_perag_t             *pag = xfs_get_perag(mp, ip->i_ino);
        unsigned long           first_index, mask;
+       unsigned long           inodes_per_cluster;
        int                     ilist_size;
        xfs_inode_t             **ilist;
        xfs_inode_t             *iq;
@@ -2997,8 +2921,9 @@ xfs_iflush_cluster(
        ASSERT(pag->pagi_inodeok);
        ASSERT(pag->pag_ici_init);
 
-       ilist_size = XFS_INODE_CLUSTER_SIZE(mp) * sizeof(xfs_inode_t *);
-       ilist = kmem_alloc(ilist_size, KM_MAYFAIL);
+       inodes_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog;
+       ilist_size = inodes_per_cluster * sizeof(xfs_inode_t *);
+       ilist = kmem_alloc(ilist_size, KM_MAYFAIL|KM_NOFS);
        if (!ilist)
                return 0;
 
@@ -3007,8 +2932,7 @@ xfs_iflush_cluster(
        read_lock(&pag->pag_ici_lock);
        /* really need a gang lookup range call here */
        nr_found = radix_tree_gang_lookup(&pag->pag_ici_root, (void**)ilist,
-                                       first_index,
-                                       XFS_INODE_CLUSTER_SIZE(mp));
+                                       first_index, inodes_per_cluster);
        if (nr_found == 0)
                goto out_free;
 
@@ -3069,7 +2993,7 @@ xfs_iflush_cluster(
 
 out_free:
        read_unlock(&pag->pag_ici_lock);
-       kmem_free(ilist, ilist_size);
+       kmem_free(ilist);
        return 0;
 
 
@@ -3113,7 +3037,7 @@ cluster_corrupt_out:
         * Unlocks the flush lock
         */
        xfs_iflush_abort(iq);
-       kmem_free(ilist, ilist_size);
+       kmem_free(ilist);
        return XFS_ERROR(EFSCORRUPTED);
 }
 
@@ -3141,7 +3065,7 @@ xfs_iflush(
 
        XFS_STATS_INC(xs_iflush_count);
 
-       ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE|MR_ACCESS));
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
        ASSERT(issemalocked(&(ip->i_flock)));
        ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE ||
               ip->i_d.di_nextents > ip->i_df.if_ext_max);
@@ -3154,8 +3078,6 @@ xfs_iflush(
         * flush lock and do nothing.
         */
        if (xfs_inode_clean(ip)) {
-               ASSERT((iip != NULL) ?
-                        !(iip->ili_item.li_flags & XFS_LI_IN_AIL) : 1);
                xfs_ifunlock(ip);
                return 0;
        }
@@ -3306,7 +3228,7 @@ xfs_iflush_int(
        int                     first;
 #endif
 
-       ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE|MR_ACCESS));
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
        ASSERT(issemalocked(&(ip->i_flock)));
        ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE ||
               ip->i_d.di_nextents > ip->i_df.if_ext_max);
@@ -3452,16 +3374,9 @@ xfs_iflush_int(
                }
        }
 
-       if (xfs_iflush_fork(ip, dip, iip, XFS_DATA_FORK, bp) == EFSCORRUPTED) {
-               goto corrupt_out;
-       }
-
-       if (XFS_IFORK_Q(ip)) {
-               /*
-                * The only error from xfs_iflush_fork is on the data fork.
-                */
-               (void) xfs_iflush_fork(ip, dip, iip, XFS_ATTR_FORK, bp);
-       }
+       xfs_iflush_fork(ip, dip, iip, XFS_DATA_FORK, bp);
+       if (XFS_IFORK_Q(ip))
+               xfs_iflush_fork(ip, dip, iip, XFS_ATTR_FORK, bp);
        xfs_inobp_check(mp, bp);
 
        /*
@@ -3546,7 +3461,6 @@ xfs_iflush_all(
        xfs_mount_t     *mp)
 {
        xfs_inode_t     *ip;
-       bhv_vnode_t     *vp;
 
  again:
        XFS_MOUNT_ILOCK(mp);
@@ -3561,14 +3475,13 @@ xfs_iflush_all(
                        continue;
                }
 
-               vp = XFS_ITOV_NULL(ip);
-               if (!vp) {
+               if (!VFS_I(ip)) {
                        XFS_MOUNT_IUNLOCK(mp);
                        xfs_finish_reclaim(ip, 0, XFS_IFLUSH_ASYNC);
                        goto again;
                }
 
-               ASSERT(vn_count(vp) == 0);
+               ASSERT(vn_count(VFS_I(ip)) == 0);
 
                ip = ip->i_mnext;
        } while (ip != mp->m_inodes);
@@ -3788,7 +3701,7 @@ xfs_iext_add_indirect_multi(
         * (all extents past */
        if (nex2) {
                byte_diff = nex2 * sizeof(xfs_bmbt_rec_t);
-               nex2_ep = (xfs_bmbt_rec_t *) kmem_alloc(byte_diff, KM_SLEEP);
+               nex2_ep = (xfs_bmbt_rec_t *) kmem_alloc(byte_diff, KM_NOFS);
                memmove(nex2_ep, &erp->er_extbuf[idx], byte_diff);
                erp->er_extcount -= nex2;
                xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, -nex2);
@@ -3854,7 +3767,7 @@ xfs_iext_add_indirect_multi(
                        erp = xfs_iext_irec_new(ifp, erp_idx);
                }
                memmove(&erp->er_extbuf[i], nex2_ep, byte_diff);
-               kmem_free(nex2_ep, byte_diff);
+               kmem_free(nex2_ep);
                erp->er_extcount += nex2;
                xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, nex2);
        }
@@ -4088,8 +4001,7 @@ xfs_iext_realloc_direct(
                        ifp->if_u1.if_extents =
                                kmem_realloc(ifp->if_u1.if_extents,
                                                rnew_size,
-                                               ifp->if_real_bytes,
-                                               KM_SLEEP);
+                                               ifp->if_real_bytes, KM_NOFS);
                }
                if (rnew_size > ifp->if_real_bytes) {
                        memset(&ifp->if_u1.if_extents[ifp->if_bytes /
@@ -4130,7 +4042,7 @@ xfs_iext_direct_to_inline(
         */
        memcpy(ifp->if_u2.if_inline_ext, ifp->if_u1.if_extents,
                nextents * sizeof(xfs_bmbt_rec_t));
-       kmem_free(ifp->if_u1.if_extents, ifp->if_real_bytes);
+       kmem_free(ifp->if_u1.if_extents);
        ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
        ifp->if_real_bytes = 0;
 }
@@ -4148,7 +4060,7 @@ xfs_iext_inline_to_direct(
        xfs_ifork_t     *ifp,           /* inode fork pointer */
        int             new_size)       /* number of extents in file */
 {
-       ifp->if_u1.if_extents = kmem_alloc(new_size, KM_SLEEP);
+       ifp->if_u1.if_extents = kmem_alloc(new_size, KM_NOFS);
        memset(ifp->if_u1.if_extents, 0, new_size);
        if (ifp->if_bytes) {
                memcpy(ifp->if_u1.if_extents, ifp->if_u2.if_inline_ext,
@@ -4180,7 +4092,7 @@ xfs_iext_realloc_indirect(
        } else {
                ifp->if_u1.if_ext_irec = (xfs_ext_irec_t *)
                        kmem_realloc(ifp->if_u1.if_ext_irec,
-                               new_size, size, KM_SLEEP);
+                               new_size, size, KM_NOFS);
        }
 }
 
@@ -4204,7 +4116,7 @@ xfs_iext_indirect_to_direct(
        ASSERT(ifp->if_real_bytes == XFS_IEXT_BUFSZ);
 
        ep = ifp->if_u1.if_ext_irec->er_extbuf;
-       kmem_free(ifp->if_u1.if_ext_irec, sizeof(xfs_ext_irec_t));
+       kmem_free(ifp->if_u1.if_ext_irec);
        ifp->if_flags &= ~XFS_IFEXTIREC;
        ifp->if_u1.if_extents = ep;
        ifp->if_bytes = size;
@@ -4230,7 +4142,7 @@ xfs_iext_destroy(
                }
                ifp->if_flags &= ~XFS_IFEXTIREC;
        } else if (ifp->if_real_bytes) {
-               kmem_free(ifp->if_u1.if_extents, ifp->if_real_bytes);
+               kmem_free(ifp->if_u1.if_extents);
        } else if (ifp->if_bytes) {
                memset(ifp->if_u2.if_inline_ext, 0, XFS_INLINE_EXTS *
                        sizeof(xfs_bmbt_rec_t));
@@ -4422,11 +4334,10 @@ xfs_iext_irec_init(
        nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
        ASSERT(nextents <= XFS_LINEAR_EXTS);
 
-       erp = (xfs_ext_irec_t *)
-               kmem_alloc(sizeof(xfs_ext_irec_t), KM_SLEEP);
+       erp = kmem_alloc(sizeof(xfs_ext_irec_t), KM_NOFS);
 
        if (nextents == 0) {
-               ifp->if_u1.if_extents = kmem_alloc(XFS_IEXT_BUFSZ, KM_SLEEP);
+               ifp->if_u1.if_extents = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
        } else if (!ifp->if_real_bytes) {
                xfs_iext_inline_to_direct(ifp, XFS_IEXT_BUFSZ);
        } else if (ifp->if_real_bytes < XFS_IEXT_BUFSZ) {
@@ -4474,7 +4385,7 @@ xfs_iext_irec_new(
 
        /* Initialize new extent record */
        erp = ifp->if_u1.if_ext_irec;
-       erp[erp_idx].er_extbuf = kmem_alloc(XFS_IEXT_BUFSZ, KM_SLEEP);
+       erp[erp_idx].er_extbuf = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
        ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ;
        memset(erp[erp_idx].er_extbuf, 0, XFS_IEXT_BUFSZ);
        erp[erp_idx].er_extcount = 0;
@@ -4501,7 +4412,7 @@ xfs_iext_irec_remove(
        if (erp->er_extbuf) {
                xfs_iext_irec_update_extoffs(ifp, erp_idx + 1,
                        -erp->er_extcount);
-               kmem_free(erp->er_extbuf, XFS_IEXT_BUFSZ);
+               kmem_free(erp->er_extbuf);
        }
        /* Compact extent records */
        erp = ifp->if_u1.if_ext_irec;
@@ -4519,8 +4430,7 @@ xfs_iext_irec_remove(
                xfs_iext_realloc_indirect(ifp,
                        nlists * sizeof(xfs_ext_irec_t));
        } else {
-               kmem_free(ifp->if_u1.if_ext_irec,
-                       sizeof(xfs_ext_irec_t));
+               kmem_free(ifp->if_u1.if_ext_irec);
        }
        ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ;
 }
@@ -4589,7 +4499,7 @@ xfs_iext_irec_compact_pages(
                         * so er_extoffs don't get modified in
                         * xfs_iext_irec_remove.
                         */
-                       kmem_free(erp_next->er_extbuf, XFS_IEXT_BUFSZ);
+                       kmem_free(erp_next->er_extbuf);
                        erp_next->er_extbuf = NULL;
                        xfs_iext_irec_remove(ifp, erp_idx + 1);
                        nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
@@ -4614,40 +4524,63 @@ xfs_iext_irec_compact_full(
        int             nlists;                 /* number of irec's (ex lists) */
 
        ASSERT(ifp->if_flags & XFS_IFEXTIREC);
+
        nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
        erp = ifp->if_u1.if_ext_irec;
        ep = &erp->er_extbuf[erp->er_extcount];
        erp_next = erp + 1;
        ep_next = erp_next->er_extbuf;
+
        while (erp_idx < nlists - 1) {
+               /*
+                * Check how many extent records are available in this irec.
+                * If there is none skip the whole exercise.
+                */
                ext_avail = XFS_LINEAR_EXTS - erp->er_extcount;
-               ext_diff = MIN(ext_avail, erp_next->er_extcount);
-               memcpy(ep, ep_next, ext_diff * sizeof(xfs_bmbt_rec_t));
-               erp->er_extcount += ext_diff;
-               erp_next->er_extcount -= ext_diff;
-               /* Remove next page */
-               if (erp_next->er_extcount == 0) {
+               if (ext_avail) {
+
                        /*
-                        * Free page before removing extent record
-                        * so er_extoffs don't get modified in
-                        * xfs_iext_irec_remove.
+                        * Copy over as many as possible extent records into
+                        * the previous page.
                         */
-                       kmem_free(erp_next->er_extbuf,
-                               erp_next->er_extcount * sizeof(xfs_bmbt_rec_t));
-                       erp_next->er_extbuf = NULL;
-                       xfs_iext_irec_remove(ifp, erp_idx + 1);
-                       erp = &ifp->if_u1.if_ext_irec[erp_idx];
-                       nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
-               /* Update next page */
-               } else {
-                       /* Move rest of page up to become next new page */
-                       memmove(erp_next->er_extbuf, ep_next,
-                               erp_next->er_extcount * sizeof(xfs_bmbt_rec_t));
-                       ep_next = erp_next->er_extbuf;
-                       memset(&ep_next[erp_next->er_extcount], 0,
-                               (XFS_LINEAR_EXTS - erp_next->er_extcount) *
-                               sizeof(xfs_bmbt_rec_t));
+                       ext_diff = MIN(ext_avail, erp_next->er_extcount);
+                       memcpy(ep, ep_next, ext_diff * sizeof(xfs_bmbt_rec_t));
+                       erp->er_extcount += ext_diff;
+                       erp_next->er_extcount -= ext_diff;
+
+                       /*
+                        * If the next irec is empty now we can simply
+                        * remove it.
+                        */
+                       if (erp_next->er_extcount == 0) {
+                               /*
+                                * Free page before removing extent record
+                                * so er_extoffs don't get modified in
+                                * xfs_iext_irec_remove.
+                                */
+                               kmem_free(erp_next->er_extbuf);
+                               erp_next->er_extbuf = NULL;
+                               xfs_iext_irec_remove(ifp, erp_idx + 1);
+                               erp = &ifp->if_u1.if_ext_irec[erp_idx];
+                               nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
+
+                       /*
+                        * If the next irec is not empty move up the content
+                        * that has not been copied to the previous page to
+                        * the beggining of this one.
+                        */
+                       } else {
+                               memmove(erp_next->er_extbuf, &ep_next[ext_diff],
+                                       erp_next->er_extcount *
+                                       sizeof(xfs_bmbt_rec_t));
+                               ep_next = erp_next->er_extbuf;
+                               memset(&ep_next[erp_next->er_extcount], 0,
+                                       (XFS_LINEAR_EXTS -
+                                               erp_next->er_extcount) *
+                                       sizeof(xfs_bmbt_rec_t));
+                       }
                }
+
                if (erp->er_extcount == XFS_LINEAR_EXTS) {
                        erp_idx++;
                        if (erp_idx < nlists)