]> Git Repo - linux.git/blobdiff - fs/xfs/xfs_inode.c
[XFS] Explain the race closed by the addition of vn_iowait() to the start
[linux.git] / fs / xfs / xfs_inode.c
index 580fa07580395631e80b0f3b613c72dcf0907a10..88a517fad07bd7a0b6d0575ccf263e611890f0f9 100644 (file)
@@ -253,7 +253,8 @@ 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_buf_t       *bp;
        int             error;
@@ -269,10 +270,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
@@ -336,9 +336,10 @@ xfs_itobp(
         * (if DEBUG kernel) or the first inode in the buffer, otherwise.
         */
 #ifdef DEBUG
-       ni = BBTOB(imap.im_len) >> mp->m_sb.sb_inodelog;
+       ni = (imap_flags & XFS_IMAP_BULKSTAT) ? 0 :
+               (BBTOB(imap.im_len) >> mp->m_sb.sb_inodelog);
 #else
-       ni = 1;
+       ni = (imap_flags & XFS_IMAP_BULKSTAT) ? 0 : 1;
 #endif
        for (i = 0; i < ni; i++) {
                int             di_ok;
@@ -505,7 +506,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:
@@ -868,9 +869,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, 0);
+       if (error) {
                kmem_zone_free(xfs_inode_zone, ip);
                return error;
        }
@@ -1372,10 +1372,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 +1393,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
@@ -1420,6 +1430,9 @@ xfs_itruncate_start(
 
        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
         * overlapping the region being removed.  We have to use
@@ -1895,7 +1908,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;
                }
@@ -2004,7 +2017,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 +2079,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.",
@@ -2723,7 +2736,7 @@ xfs_iunpin(
 
                /* make sync come back and flush this inode */
                if (vp) {
-                       struct inode    *inode = LINVFS_GET_IP(vp);
+                       struct inode    *inode = vn_to_inode(vp);
 
                        if (!(inode->i_state & I_NEW))
                                mark_inode_dirty_sync(inode);
@@ -3023,8 +3036,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;
        }
@@ -3519,7 +3532,7 @@ xfs_iaccess(
 {
        int             error;
        mode_t          orgmode = mode;
-       struct inode    *inode = LINVFS_GET_IP(XFS_ITOV(ip));
+       struct inode    *inode = vn_to_inode(XFS_ITOV(ip));
 
        if (mode & S_IWUSR) {
                umode_t         imode = inode->i_mode;
@@ -4265,12 +4278,81 @@ xfs_iext_destroy(
        ifp->if_bytes = 0;
 }
 
+/*
+ * Return a pointer to the extent record for file system block bno.
+ */
+xfs_bmbt_rec_t *                       /* pointer to found extent record */
+xfs_iext_bno_to_ext(
+       xfs_ifork_t     *ifp,           /* inode fork pointer */
+       xfs_fileoff_t   bno,            /* block number to search for */
+       xfs_extnum_t    *idxp)          /* index of target extent */
+{
+       xfs_bmbt_rec_t  *base;          /* pointer to first extent */
+       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 */
+       xfs_extnum_t    idx = 0;        /* index of target extent */
+       int             low;            /* lower boundry in search */
+       xfs_extnum_t    nextents;       /* number of file extents */
+       xfs_fileoff_t   startoff = 0;   /* start offset of extent */
+
+       nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+       if (nextents == 0) {
+               *idxp = 0;
+               return NULL;
+       }
+       low = 0;
+       if (ifp->if_flags & XFS_IFEXTIREC) {
+               /* Find target extent list */
+               int     erp_idx = 0;
+               erp = xfs_iext_bno_to_irec(ifp, bno, &erp_idx);
+               base = erp->er_extbuf;
+               high = erp->er_extcount - 1;
+       } else {
+               base = ifp->if_u1.if_extents;
+               high = nextents - 1;
+       }
+       /* Binary search extent records */
+       while (low <= high) {
+               idx = (low + high) >> 1;
+               ep = base + idx;
+               startoff = xfs_bmbt_get_startoff(ep);
+               blockcount = xfs_bmbt_get_blockcount(ep);
+               if (bno < startoff) {
+                       high = idx - 1;
+               } else if (bno >= startoff + blockcount) {
+                       low = idx + 1;
+               } else {
+                       /* Convert back to file-based extent index */
+                       if (ifp->if_flags & XFS_IFEXTIREC) {
+                               idx += erp->er_extoff;
+                       }
+                       *idxp = idx;
+                       return ep;
+               }
+       }
+       /* Convert back to file-based extent index */
+       if (ifp->if_flags & XFS_IFEXTIREC) {
+               idx += erp->er_extoff;
+       }
+       if (bno >= startoff + blockcount) {
+               if (++idx == nextents) {
+                       ep = NULL;
+               } else {
+                       ep = xfs_iext_get_ext(ifp, idx);
+               }
+       }
+       *idxp = idx;
+       return ep;
+}
+
 /*
  * Return a pointer to the indirection array entry containing the
  * extent record for filesystem block bno. Store the index of the
  * target irec in *erp_idxp.
  */
-xfs_ext_irec_t *
+xfs_ext_irec_t *                       /* pointer to found extent record */
 xfs_iext_bno_to_irec(
        xfs_ifork_t     *ifp,           /* inode fork pointer */
        xfs_fileoff_t   bno,            /* block number to search for */
@@ -4278,7 +4360,7 @@ xfs_iext_bno_to_irec(
 {
        xfs_ext_irec_t  *erp = NULL;    /* indirection array pointer */
        xfs_ext_irec_t  *erp_next;      /* next indirection array entry */
-       xfs_extnum_t    erp_idx;        /* indirection array index */
+       int             erp_idx;        /* indirection array index */
        int             nlists;         /* number of extent irec's (lists) */
        int             high;           /* binary search upper limit */
        int             low;            /* binary search lower limit */
This page took 0.035549 seconds and 4 git commands to generate.