Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[safe/jmp/linux-2.6] / fs / xfs / xfs_inode.c
index 86a8451..3ca5d43 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
  * All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or
 #include "xfs_trans_priv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_dir.h"
 #include "xfs_dir2.h"
 #include "xfs_dmapi.h"
 #include "xfs_mount.h"
 #include "xfs_bmap_btree.h"
 #include "xfs_alloc_btree.h"
 #include "xfs_ialloc_btree.h"
-#include "xfs_dir_sf.h"
 #include "xfs_dir2_sf.h"
 #include "xfs_attr_sf.h"
 #include "xfs_dinode.h"
@@ -49,7 +47,6 @@
 #include "xfs_utils.h"
 #include "xfs_dir2_trace.h"
 #include "xfs_quota.h"
-#include "xfs_mac.h"
 #include "xfs_acl.h"
 
 
@@ -160,7 +157,7 @@ xfs_inotobp(
        xfs_dinode_t    *dip;
 
        /*
-        * Call the space managment code to find the location of the
+        * Call the space management code to find the location of the
         * inode on disk.
         */
        imap.im_blkno = 0;
@@ -253,15 +250,14 @@ xfs_itobp(
        xfs_inode_t     *ip,
        xfs_dinode_t    **dipp,
        xfs_buf_t       **bpp,
-       xfs_daddr_t     bno)
+       xfs_daddr_t     bno,
+       uint            imap_flags)
 {
+       xfs_imap_t      imap;
        xfs_buf_t       *bp;
        int             error;
-       xfs_imap_t      imap;
-#ifdef __KERNEL__
        int             i;
        int             ni;
-#endif
 
        if (ip->i_blkno == (xfs_daddr_t)0) {
                /*
@@ -269,10 +265,9 @@ xfs_itobp(
                 * inode on disk.
                 */
                imap.im_blkno = bno;
-               error = xfs_imap(mp, tp, ip->i_ino, &imap, XFS_IMAP_LOOKUP);
-               if (error != 0) {
+               if ((error = xfs_imap(mp, tp, ip->i_ino, &imap,
+                                       XFS_IMAP_LOOKUP | imap_flags)))
                        return error;
-               }
 
                /*
                 * If the inode number maps to a block outside the bounds
@@ -319,7 +314,6 @@ xfs_itobp(
         */
        error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, imap.im_blkno,
                                   (int)imap.im_len, XFS_BUF_LOCK, &bp);
-
        if (error) {
 #ifdef DEBUG
                xfs_fs_cmn_err(CE_ALERT, mp, "xfs_itobp: "
@@ -330,16 +324,20 @@ xfs_itobp(
 #endif /* DEBUG */
                return error;
        }
-#ifdef __KERNEL__
+
        /*
         * Validate the magic number and version of every inode in the buffer
         * (if DEBUG kernel) or the first inode in the buffer, otherwise.
+        * No validation is done here in userspace (xfs_repair).
         */
-#ifdef DEBUG
+#if !defined(__KERNEL__)
+       ni = 0;
+#elif defined(DEBUG)
        ni = BBTOB(imap.im_len) >> mp->m_sb.sb_inodelog;
-#else
+#else  /* usual case */
        ni = 1;
 #endif
+
        for (i = 0; i < ni; i++) {
                int             di_ok;
                xfs_dinode_t    *dip;
@@ -348,11 +346,18 @@ xfs_itobp(
                                        (i << mp->m_sb.sb_inodelog));
                di_ok = INT_GET(dip->di_core.di_magic, ARCH_CONVERT) == XFS_DINODE_MAGIC &&
                            XFS_DINODE_GOOD_VERSION(INT_GET(dip->di_core.di_version, ARCH_CONVERT));
-               if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP,
-                                XFS_RANDOM_ITOBP_INOTOBP))) {
+               if (unlikely(XFS_TEST_ERROR(!di_ok, mp,
+                                               XFS_ERRTAG_ITOBP_INOTOBP,
+                                               XFS_RANDOM_ITOBP_INOTOBP))) {
+                       if (imap_flags & XFS_IMAP_BULKSTAT) {
+                               xfs_trans_brelse(tp, bp);
+                               return XFS_ERROR(EINVAL);
+                       }
 #ifdef DEBUG
-                       prdev("bad inode magic/vsn daddr %lld #%d (magic=%x)",
-                               mp->m_ddev_targp,
+                       cmn_err(CE_ALERT,
+                                       "Device %s - bad inode magic/vsn "
+                                       "daddr %lld #%d (magic=%x)",
+                               XFS_BUFTARG_NAME(mp->m_ddev_targp),
                                (unsigned long long)imap.im_blkno, i,
                                INT_GET(dip->di_core.di_magic, ARCH_CONVERT));
 #endif
@@ -362,7 +367,6 @@ xfs_itobp(
                        return XFS_ERROR(EFSCORRUPTED);
                }
        }
-#endif /* __KERNEL__ */
 
        xfs_inobp_check(mp, bp);
 
@@ -438,6 +442,7 @@ xfs_iformat(
                        return XFS_ERROR(EFSCORRUPTED);
                }
                ip->i_d.di_size = 0;
+               ip->i_size = 0;
                ip->i_df.if_u2.if_rdev = INT_GET(dip->di_u.di_dev, ARCH_CONVERT);
                break;
 
@@ -505,7 +510,7 @@ xfs_iformat(
        switch (INT_GET(dip->di_core.di_aformat, ARCH_CONVERT)) {
        case XFS_DINODE_FMT_LOCAL:
                atp = (xfs_attr_shortform_t *)XFS_DFORK_APTR(dip);
-               size = (int)INT_GET(atp->hdr.totsize, ARCH_CONVERT);
+               size = be16_to_cpu(atp->hdr.totsize);
                error = xfs_iformat_local(ip, dip, XFS_ATTR_FORK, size);
                break;
        case XFS_DINODE_FMT_EXTENTS:
@@ -781,7 +786,6 @@ xfs_xlate_dinode_core(
 
 STATIC uint
 _xfs_dic2xflags(
-       xfs_dinode_core_t       *dic,
        __uint16_t              di_flags)
 {
        uint                    flags = 0;
@@ -811,6 +815,8 @@ _xfs_dic2xflags(
                        flags |= XFS_XFLAG_EXTSIZE;
                if (di_flags & XFS_DIFLAG_EXTSZINHERIT)
                        flags |= XFS_XFLAG_EXTSZINHERIT;
+               if (di_flags & XFS_DIFLAG_NODEFRAG)
+                       flags |= XFS_XFLAG_NODEFRAG;
        }
 
        return flags;
@@ -822,21 +828,21 @@ xfs_ip2xflags(
 {
        xfs_dinode_core_t       *dic = &ip->i_d;
 
-       return _xfs_dic2xflags(dic, dic->di_flags) |
-               (XFS_CFORK_Q(dic) ? XFS_XFLAG_HASATTR : 0);
+       return _xfs_dic2xflags(dic->di_flags) |
+                               (XFS_CFORK_Q(dic) ? XFS_XFLAG_HASATTR : 0);
 }
 
 uint
 xfs_dic2xflags(
        xfs_dinode_core_t       *dic)
 {
-       return _xfs_dic2xflags(dic, INT_GET(dic->di_flags, ARCH_CONVERT)) |
-               (XFS_CFORK_Q_DISK(dic) ? XFS_XFLAG_HASATTR : 0);
+       return _xfs_dic2xflags(INT_GET(dic->di_flags, ARCH_CONVERT)) |
+                               (XFS_CFORK_Q_DISK(dic) ? XFS_XFLAG_HASATTR : 0);
 }
 
 /*
  * Given a mount structure and an inode number, return a pointer
- * to a newly allocated in-core inode coresponding to the given
+ * to a newly allocated in-core inode corresponding to the given
  * inode number.
  *
  * Initialize the inode's attributes and extent pointers if it
@@ -848,7 +854,8 @@ xfs_iread(
        xfs_trans_t     *tp,
        xfs_ino_t       ino,
        xfs_inode_t     **ipp,
-       xfs_daddr_t     bno)
+       xfs_daddr_t     bno,
+       uint            imap_flags)
 {
        xfs_buf_t       *bp;
        xfs_dinode_t    *dip;
@@ -860,6 +867,7 @@ xfs_iread(
        ip = kmem_zone_zalloc(xfs_inode_zone, KM_SLEEP);
        ip->i_ino = ino;
        ip->i_mount = mp;
+       spin_lock_init(&ip->i_flags_lock);
 
        /*
         * Get pointer's to the on-disk inode and the buffer containing it.
@@ -868,9 +876,8 @@ xfs_iread(
         * return NULL as well.  Set i_blkno to 0 so that xfs_itobp() will
         * know that this is a new incore inode.
         */
-       error = xfs_itobp(mp, tp, ip, &dip, &bp, bno);
-
-       if (error != 0) {
+       error = xfs_itobp(mp, tp, ip, &dip, &bp, bno, imap_flags);
+       if (error) {
                kmem_zone_free(xfs_inode_zone, ip);
                return error;
        }
@@ -974,6 +981,7 @@ xfs_iread(
        }
 
        ip->i_delayed_blks = 0;
+       ip->i_size = ip->i_d.di_size;
 
        /*
         * Mark the buffer containing the inode as something to keep
@@ -1083,7 +1091,7 @@ xfs_ialloc(
 {
        xfs_ino_t       ino;
        xfs_inode_t     *ip;
-       vnode_t         *vp;
+       bhv_vnode_t     *vp;
        uint            flags;
        int             error;
 
@@ -1108,7 +1116,7 @@ xfs_ialloc(
         * to prevent others from looking at until we're done.
         */
        error = xfs_trans_iget(tp->t_mountp, tp, ino,
-                       IGET_CREATE, XFS_ILOCK_EXCL, &ip);
+                               XFS_IGET_CREATE, XFS_ILOCK_EXCL, &ip);
        if (error != 0) {
                return error;
        }
@@ -1164,6 +1172,7 @@ xfs_ialloc(
        }
 
        ip->i_d.di_size = 0;
+       ip->i_size = 0;
        ip->i_d.di_nextents = 0;
        ASSERT(ip->i_d.di_nblocks == 0);
        xfs_ichgtime(ip, XFS_ICHGTIME_CHG|XFS_ICHGTIME_ACC|XFS_ICHGTIME_MOD);
@@ -1221,6 +1230,9 @@ xfs_ialloc(
                                di_flags |= XFS_DIFLAG_NOSYMLINKS;
                        if (pip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)
                                di_flags |= XFS_DIFLAG_PROJINHERIT;
+                       if ((pip->i_d.di_flags & XFS_DIFLAG_NODEFRAG) &&
+                           xfs_inherit_nodefrag)
+                               di_flags |= XFS_DIFLAG_NODEFRAG;
                        ip->i_d.di_flags |= di_flags;
                }
                /* FALLTHROUGH */
@@ -1244,8 +1256,8 @@ xfs_ialloc(
         */
        xfs_trans_log_inode(tp, ip, flags);
 
-       /* now that we have an i_mode  we can set Linux inode ops (& unlock) */
-       VFS_INIT_VNODE(XFS_MTOVFS(tp->t_mountp), vp, XFS_ITOBHV(ip), 1);
+       /* now that we have an i_mode we can setup inode ops and unlock */
+       bhv_vfs_init_vnode(XFS_MTOVFS(tp->t_mountp), vp, XFS_ITOBHV(ip), 1);
 
        *ipp = ip;
        return 0;
@@ -1285,7 +1297,7 @@ xfs_isize_check(
                                       (xfs_ufsize_t)XFS_MAXIOFFSET(mp)) -
                          map_first),
                         XFS_BMAPI_ENTIRE, NULL, 0, imaps, &nimaps,
-                        NULL))
+                        NULL, NULL))
            return;
        ASSERT(nimaps == 1);
        ASSERT(imaps[0].br_startblock == HOLESTARTBLOCK);
@@ -1331,7 +1343,7 @@ xfs_file_last_byte(
        } else {
                last_block = 0;
        }
-       size_last_block = XFS_B_TO_FSB(mp, (xfs_ufsize_t)ip->i_d.di_size);
+       size_last_block = XFS_B_TO_FSB(mp, (xfs_ufsize_t)ip->i_size);
        last_block = XFS_FILEOFF_MAX(last_block, size_last_block);
 
        last_byte = XFS_FSB_TO_B(mp, last_block);
@@ -1372,10 +1384,10 @@ xfs_itrunc_trace(
                     (void*)(unsigned long)((toss_finish >> 32) & 0xffffffff),
                     (void*)(unsigned long)(toss_finish & 0xffffffff),
                     (void*)(unsigned long)current_cpu(),
-                    (void*)0,
-                    (void*)0,
-                    (void*)0,
-                    (void*)0);
+                    (void*)(unsigned long)current_pid(),
+                    (void*)NULL,
+                    (void*)NULL,
+                    (void*)NULL);
 }
 #else
 #define        xfs_itrunc_trace(tag, ip, flag, new_size, toss_start, toss_finish)
@@ -1393,6 +1405,16 @@ xfs_itrunc_trace(
  * calling into the buffer/page cache code and we can't hold the
  * inode lock when we do so.
  *
+ * We need to wait for any direct I/Os in flight to complete before we
+ * proceed with the truncate. This is needed to prevent the extents
+ * being read or written by the direct I/Os from being removed while the
+ * I/O is in flight as there is no other method of synchronising
+ * direct I/O with the truncate operation.  Also, because we hold
+ * the IOLOCK in exclusive mode, we prevent new direct I/Os from being
+ * started until the truncate completes and drops the lock. Essentially,
+ * the vn_iowait() call forms an I/O barrier that provides strict ordering
+ * between direct I/Os and the truncate operation.
+ *
  * The flags parameter can have either the value XFS_ITRUNC_DEFINITE
  * or XFS_ITRUNC_MAYBE.  The XFS_ITRUNC_MAYBE value should be used
  * in the case that the caller is locking things out of order and
@@ -1402,7 +1424,7 @@ xfs_itrunc_trace(
  * must be called again with all the same restrictions as the initial
  * call.
  */
-void
+int
 xfs_itruncate_start(
        xfs_inode_t     *ip,
        uint            flags,
@@ -1411,19 +1433,23 @@ xfs_itruncate_start(
        xfs_fsize_t     last_byte;
        xfs_off_t       toss_start;
        xfs_mount_t     *mp;
-       vnode_t         *vp;
+       bhv_vnode_t     *vp;
+       int             error = 0;
 
        ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE) != 0);
-       ASSERT((new_size == 0) || (new_size <= ip->i_d.di_size));
+       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);
+
+       vn_iowait(vp);  /* wait for the completion of any pending DIOs */
+       
        /*
-        * Call VOP_TOSS_PAGES() or VOP_FLUSHINVAL_PAGES() to get rid of pages and buffers
+        * Call toss_pages or flushinval_pages to get rid of pages
         * overlapping the region being removed.  We have to use
-        * the less efficient VOP_FLUSHINVAL_PAGES() in the case that the
+        * the less efficient flushinval_pages in the case that the
         * caller may not be able to finish the truncate without
         * dropping the inode's I/O lock.  Make sure
         * to catch any pages brought in by buffers overlapping
@@ -1432,10 +1458,10 @@ xfs_itruncate_start(
         * so that we don't toss things on the same block as
         * new_size but before it.
         *
-        * Before calling VOP_TOSS_PAGES() or VOP_FLUSHINVAL_PAGES(), make sure to
+        * Before calling toss_page or flushinval_pages, make sure to
         * call remapf() over the same region if the file is mapped.
         * This frees up mapped file references to the pages in the
-        * given range and for the VOP_FLUSHINVAL_PAGES() case it ensures
+        * given range and for the flushinval_pages case it ensures
         * that we get the latest mapped changes flushed out.
         */
        toss_start = XFS_B_TO_FSB(mp, (xfs_ufsize_t)new_size);
@@ -1446,16 +1472,16 @@ xfs_itruncate_start(
                 * file size, so there is no way that the data extended
                 * out there.
                 */
-               return;
+               return 0;
        }
        last_byte = xfs_file_last_byte(ip);
        xfs_itrunc_trace(XFS_ITRUNC_START, ip, flags, new_size, toss_start,
                         last_byte);
        if (last_byte > toss_start) {
                if (flags & XFS_ITRUNC_DEFINITE) {
-                       VOP_TOSS_PAGES(vp, toss_start, -1, FI_REMAPF_LOCKED);
+                       bhv_vop_toss_pages(vp, toss_start, -1, FI_REMAPF_LOCKED);
                } else {
-                       VOP_FLUSHINVAL_PAGES(vp, toss_start, -1, FI_REMAPF_LOCKED);
+                       error = bhv_vop_flushinval_pages(vp, toss_start, -1, FI_REMAPF_LOCKED);
                }
        }
 
@@ -1464,6 +1490,7 @@ xfs_itruncate_start(
                ASSERT(VN_CACHED(vp) == 0);
        }
 #endif
+       return error;
 }
 
 /*
@@ -1534,7 +1561,7 @@ xfs_itruncate_finish(
 
        ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE) != 0);
        ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE) != 0);
-       ASSERT((new_size == 0) || (new_size <= ip->i_d.di_size));
+       ASSERT((new_size == 0) || (new_size <= ip->i_size));
        ASSERT(*tp != NULL);
        ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES);
        ASSERT(ip->i_transp == *tp);
@@ -1608,8 +1635,20 @@ xfs_itruncate_finish(
         */
        if (fork == XFS_DATA_FORK) {
                if (ip->i_d.di_nextents > 0) {
-                       ip->i_d.di_size = new_size;
-                       xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
+                       /*
+                        * If we are not changing the file size then do
+                        * not update the on-disk file size - we may be
+                        * called from xfs_inactive_free_eofblocks().  If we
+                        * update the on-disk file size and then the system
+                        * crashes before the contents of the file are
+                        * flushed to disk then the files may be full of
+                        * holes (ie NULL files bug).
+                        */
+                       if (ip->i_size != new_size) {
+                               ip->i_d.di_size = new_size;
+                               ip->i_size = new_size;
+                               xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
+                       }
                }
        } else if (sync) {
                ASSERT(!(mp->m_flags & XFS_MOUNT_WSYNC));
@@ -1653,12 +1692,13 @@ xfs_itruncate_finish(
                 * runs.
                 */
                XFS_BMAP_INIT(&free_list, &first_block);
-               error = xfs_bunmapi(ntp, ip, first_unmap_block,
-                                   unmap_len,
+               error = XFS_BUNMAPI(mp, ntp, &ip->i_iocore,
+                                   first_unmap_block, unmap_len,
                                    XFS_BMAPI_AFLAG(fork) |
                                      (sync ? 0 : XFS_BMAPI_ASYNC),
                                    XFS_ITRUNC_MAX_EXTENTS,
-                                   &first_block, &free_list, &done);
+                                   &first_block, &free_list,
+                                   NULL, &done);
                if (error) {
                        /*
                         * If the bunmapi call encounters an error,
@@ -1675,8 +1715,7 @@ xfs_itruncate_finish(
                 * Duplicate the transaction that has the permanent
                 * reservation and commit the old transaction.
                 */
-               error = xfs_bmap_finish(tp, &free_list, first_block,
-                                       &committed);
+               error = xfs_bmap_finish(tp, &free_list, &committed);
                ntp = *tp;
                if (error) {
                        /*
@@ -1724,7 +1763,7 @@ xfs_itruncate_finish(
                        xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
                }
                ntp = xfs_trans_dup(ntp);
-               (void) xfs_trans_commit(*tp, 0, NULL);
+               (void) xfs_trans_commit(*tp, 0);
                *tp = ntp;
                error = xfs_trans_reserve(ntp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,
                                          XFS_TRANS_PERM_LOG_RES,
@@ -1745,7 +1784,19 @@ xfs_itruncate_finish(
         */
        if (fork == XFS_DATA_FORK) {
                xfs_isize_check(mp, ip, new_size);
-               ip->i_d.di_size = new_size;
+               /*
+                * If we are not changing the file size then do
+                * not update the on-disk file size - we may be
+                * called from xfs_inactive_free_eofblocks().  If we
+                * update the on-disk file size and then the system
+                * crashes before the contents of the file are
+                * flushed to disk then the files may be full of
+                * holes (ie NULL files bug).
+                */
+               if (ip->i_size != new_size) {
+                       ip->i_d.di_size = new_size;
+                       ip->i_size = new_size;
+               }
        }
        xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
        ASSERT((new_size != 0) ||
@@ -1778,7 +1829,7 @@ xfs_igrow_start(
 
        ASSERT(ismrlocked(&(ip->i_lock), MR_UPDATE) != 0);
        ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE) != 0);
-       ASSERT(new_size > ip->i_d.di_size);
+       ASSERT(new_size > ip->i_size);
 
        /*
         * Zero any pages that may have been created by
@@ -1786,7 +1837,7 @@ xfs_igrow_start(
         * and any blocks between the old and new file sizes.
         */
        error = xfs_zero_eof(XFS_ITOV(ip), &ip->i_iocore, new_size,
-                            ip->i_d.di_size, new_size);
+                            ip->i_size);
        return error;
 }
 
@@ -1810,13 +1861,14 @@ xfs_igrow_finish(
        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_d.di_size);
+       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);
@@ -1895,7 +1947,7 @@ xfs_iunlink(
                 * Here we put the head pointer into our next pointer,
                 * and then we fall through to point the head at us.
                 */
-               error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0);
+               error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0, 0);
                if (error) {
                        return error;
                }
@@ -1942,9 +1994,9 @@ xfs_iunlink_remove(
        xfs_agino_t     agino;
        xfs_agino_t     next_agino;
        xfs_buf_t       *last_ibp;
-       xfs_dinode_t    *last_dip;
+       xfs_dinode_t    *last_dip = NULL;
        short           bucket_index;
-       int             offset, last_offset;
+       int             offset, last_offset = 0;
        int             error;
        int             agi_ok;
 
@@ -2004,7 +2056,7 @@ xfs_iunlink_remove(
                 * of dealing with the buffer when there is no need to
                 * change it.
                 */
-               error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0);
+               error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0, 0);
                if (error) {
                        cmn_err(CE_WARN,
                                "xfs_iunlink_remove: xfs_itobp()  returned an error %d on %s.  Returning error.",
@@ -2066,7 +2118,7 @@ xfs_iunlink_remove(
                 * Now last_ibp points to the buffer previous to us on
                 * the unlinked list.  Pull us from the list.
                 */
-               error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0);
+               error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0, 0);
                if (error) {
                        cmn_err(CE_WARN,
                                "xfs_iunlink_remove: xfs_itobp()  returned an error %d on %s.  Returning error.",
@@ -2101,7 +2153,7 @@ xfs_iunlink_remove(
        return 0;
 }
 
-static __inline__ int xfs_inode_clean(xfs_inode_t *ip)
+STATIC_INLINE int xfs_inode_clean(xfs_inode_t *ip)
 {
        return (((ip->i_itemp == NULL) ||
                !(ip->i_itemp->ili_format.ilf_fields & XFS_ILOG_ALL)) &&
@@ -2169,7 +2221,7 @@ xfs_ifree_cluster(
                        /* Inode not in memory or we found it already,
                         * nothing to do
                         */
-                       if (!ip || (ip->i_flags & XFS_ISTALE)) {
+                       if (!ip || xfs_iflags_test(ip, XFS_ISTALE)) {
                                read_unlock(&ih->ih_lock);
                                continue;
                        }
@@ -2191,8 +2243,7 @@ xfs_ifree_cluster(
 
                        if (ip == free_ip) {
                                if (xfs_iflock_nowait(ip)) {
-                                       ip->i_flags |= XFS_ISTALE;
-
+                                       xfs_iflags_set(ip, XFS_ISTALE);
                                        if (xfs_inode_clean(ip)) {
                                                xfs_ifunlock(ip);
                                        } else {
@@ -2205,7 +2256,7 @@ xfs_ifree_cluster(
 
                        if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) {
                                if (xfs_iflock_nowait(ip)) {
-                                       ip->i_flags |= XFS_ISTALE;
+                                       xfs_iflags_set(ip, XFS_ISTALE);
 
                                        if (xfs_inode_clean(ip)) {
                                                xfs_ifunlock(ip);
@@ -2235,7 +2286,7 @@ xfs_ifree_cluster(
                                AIL_LOCK(mp,s);
                                iip->ili_flush_lsn = iip->ili_item.li_lsn;
                                AIL_UNLOCK(mp, s);
-                               iip->ili_inode->i_flags |= XFS_ISTALE;
+                               xfs_iflags_set(iip->ili_inode, XFS_ISTALE);
                                pre_flushed++;
                        }
                        lip = lip->li_bio_list;
@@ -2300,7 +2351,7 @@ xfs_ifree(
        ASSERT(ip->i_d.di_nlink == 0);
        ASSERT(ip->i_d.di_nextents == 0);
        ASSERT(ip->i_d.di_anextents == 0);
-       ASSERT((ip->i_d.di_size == 0) ||
+       ASSERT((ip->i_d.di_size == 0 && ip->i_size == 0) ||
               ((ip->i_d.di_mode & S_IFMT) != S_IFREG));
        ASSERT(ip->i_d.di_nblocks == 0);
 
@@ -2684,10 +2735,24 @@ xfs_idestroy(
        ktrace_free(ip->i_dir_trace);
 #endif
        if (ip->i_itemp) {
-               /* XXXdpd should be able to assert this but shutdown
-                * is leaving the AIL behind. */
-               ASSERT(((ip->i_itemp->ili_item.li_flags & XFS_LI_IN_AIL) == 0) ||
-                      XFS_FORCED_SHUTDOWN(ip->i_mount));
+               /*
+                * Only if we are shutting down the fs will we see an
+                * inode still in the AIL. If it is there, we should remove
+                * it to prevent a use-after-free from occurring.
+                */
+               xfs_mount_t     *mp = ip->i_mount;
+               xfs_log_item_t  *lip = &ip->i_itemp->ili_item;
+               int             s;
+
+               ASSERT(((lip->li_flags & XFS_LI_IN_AIL) == 0) ||
+                                      XFS_FORCED_SHUTDOWN(ip->i_mount));
+               if (lip->li_flags & XFS_LI_IN_AIL) {
+                       AIL_LOCK(mp, s);
+                       if (lip->li_flags & XFS_LI_IN_AIL)
+                               xfs_trans_delete_ail(mp, lip, s);
+                       else
+                               AIL_UNLOCK(mp, s);
+               }
                xfs_inode_item_destroy(ip);
        }
        kmem_zone_free(xfs_inode_zone, ip);
@@ -2710,7 +2775,7 @@ xfs_ipin(
 /*
  * Decrement the pin count of the given inode, and wake up
  * anyone in xfs_iwait_unpin() if the count goes to 0.  The
- * inode must have been previoulsy pinned with a call to xfs_ipin().
+ * inode must have been previously pinned with a call to xfs_ipin().
  */
 void
 xfs_iunpin(
@@ -2718,17 +2783,38 @@ xfs_iunpin(
 {
        ASSERT(atomic_read(&ip->i_pincount) > 0);
 
-       if (atomic_dec_and_test(&ip->i_pincount)) {
-               vnode_t *vp = XFS_ITOV_NULL(ip);
+       if (atomic_dec_and_lock(&ip->i_pincount, &ip->i_flags_lock)) {
 
-               /* make sync come back and flush this inode */
-               if (vp) {
-                       struct inode    *inode = vn_to_inode(vp);
+               /*
+                * If the inode is currently being reclaimed, the link between
+                * the bhv_vnode and the xfs_inode will be broken after the
+                * XFS_IRECLAIM* flag is set. Hence, if these flags are not
+                * set, then we can move forward and mark the linux inode dirty
+                * knowing that it is still valid as it won't freed until after
+                * the bhv_vnode<->xfs_inode link is broken in xfs_reclaim. The
+                * i_flags_lock is used to synchronise the setting of the
+                * XFS_IRECLAIM* flags and the breaking of the link, and so we
+                * can execute atomically w.r.t to reclaim by holding this lock
+                * here.
+                *
+                * However, we still need to issue the unpin wakeup call as the
+                * inode reclaim may be blocked waiting for the inode to become
+                * unpinned.
+                */
 
-                       if (!(inode->i_state & I_NEW))
+               if (!__xfs_iflags_test(ip, XFS_IRECLAIM|XFS_IRECLAIMABLE)) {
+                       bhv_vnode_t     *vp = XFS_ITOV_NULL(ip);
+                       struct inode *inode = NULL;
+
+                       BUG_ON(vp == NULL);
+                       inode = vn_to_inode(vp);
+                       BUG_ON(inode->i_state & I_CLEAR);
+
+                       /* make sync come back and flush this inode */
+                       if (!(inode->i_state & (I_NEW|I_FREEING)))
                                mark_inode_dirty_sync(inode);
                }
-
+               spin_unlock(&ip->i_flags_lock);
                wake_up(&ip->i_ipin_wait);
        }
 }
@@ -2890,13 +2976,6 @@ xfs_iflush_fork(
                        ASSERT(ifp->if_bytes <= XFS_IFORK_SIZE(ip, whichfork));
                        memcpy(cp, ifp->if_u1.if_data, ifp->if_bytes);
                }
-               if (whichfork == XFS_DATA_FORK) {
-                       if (unlikely(XFS_DIR_SHORTFORM_VALIDATE_ONDISK(mp, dip))) {
-                               XFS_ERROR_REPORT("xfs_iflush_fork",
-                                                XFS_ERRLEVEL_LOW, mp);
-                               return XFS_ERROR(EFSCORRUPTED);
-                       }
-               }
                break;
 
        case XFS_DINODE_FMT_EXTENTS:
@@ -2980,7 +3059,7 @@ xfs_iflush(
        XFS_STATS_INC(xs_iflush_count);
 
        ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE|MR_ACCESS));
-       ASSERT(valusema(&ip->i_flock) <= 0);
+       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);
 
@@ -3023,8 +3102,8 @@ xfs_iflush(
        /*
         * Get the buffer containing the on-disk inode.
         */
-       error = xfs_itobp(mp, NULL, ip, &dip, &bp, 0);
-       if (error != 0) {
+       error = xfs_itobp(mp, NULL, ip, &dip, &bp, 0, 0);
+       if (error) {
                xfs_ifunlock(ip);
                return error;
        }
@@ -3173,7 +3252,7 @@ xfs_iflush(
 
 corrupt_out:
        xfs_buf_relse(bp);
-       xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
+       xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
        xfs_iflush_abort(ip);
        /*
         * Unlocks the flush lock
@@ -3195,7 +3274,7 @@ cluster_corrupt_out:
                xfs_buf_relse(bp);
        }
 
-       xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
+       xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
 
        if(!bufwasdelwri)  {
                /*
@@ -3238,7 +3317,7 @@ xfs_iflush_int(
        SPLDECL(s);
 
        ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE|MR_ACCESS));
-       ASSERT(valusema(&ip->i_flock) <= 0);
+       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);
 
@@ -3478,7 +3557,7 @@ xfs_iflush_all(
        xfs_mount_t     *mp)
 {
        xfs_inode_t     *ip;
-       vnode_t         *vp;
+       bhv_vnode_t     *vp;
 
  again:
        XFS_MOUNT_ILOCK(mp);
@@ -3677,7 +3756,7 @@ void
 xfs_iext_add(
        xfs_ifork_t     *ifp,           /* inode fork pointer */
        xfs_extnum_t    idx,            /* index to begin adding exts */
-       int             ext_diff)       /* nubmer of extents to add */
+       int             ext_diff)       /* number of extents to add */
 {
        int             byte_diff;      /* new bytes being added */
        int             new_size;       /* size of extents after adding */
@@ -4025,7 +4104,7 @@ xfs_iext_remove_indirect(
        xfs_extnum_t    ext_diff;       /* extents to remove in current list */
        xfs_extnum_t    nex1;           /* number of extents before idx */
        xfs_extnum_t    nex2;           /* extents after idx + count */
-       int             nlists;         /* entries in indirecton array */
+       int             nlists;         /* entries in indirection array */
        int             page_idx = idx; /* index in target extent list */
 
        ASSERT(ifp->if_flags & XFS_IFEXTIREC);
@@ -4154,7 +4233,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, KM_SLEEP);
+       kmem_free(ifp->if_u1.if_extents, ifp->if_real_bytes);
        ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
        ifp->if_real_bytes = 0;
 }
@@ -4278,9 +4357,9 @@ xfs_iext_bno_to_ext(
        xfs_filblks_t   blockcount = 0; /* number of blocks in extent */
        xfs_bmbt_rec_t  *ep = NULL;     /* pointer to target extent */
        xfs_ext_irec_t  *erp = NULL;    /* indirection array pointer */
-       int             high;           /* upper boundry in search */
+       int             high;           /* upper boundary in search */
        xfs_extnum_t    idx = 0;        /* index of target extent */
-       int             low;            /* lower boundry in search */
+       int             low;            /* lower boundary in search */
        xfs_extnum_t    nextents;       /* number of file extents */
        xfs_fileoff_t   startoff = 0;   /* start offset of extent */