Merge branch 'drm-fixes' of git://people.freedesktop.org/~airlied/linux
[firefly-linux-kernel-4.4.55.git] / fs / xfs / xfs_ialloc.c
index 515bf71ce01ce790e0bab557384a0dfb07c3544a..c8f5ae1debf2aed95fd272eadd4ec2dd4eaba13c 100644 (file)
@@ -36,6 +36,8 @@
 #include "xfs_rtalloc.h"
 #include "xfs_error.h"
 #include "xfs_bmap.h"
+#include "xfs_cksum.h"
+#include "xfs_buf_item.h"
 
 
 /*
@@ -165,6 +167,7 @@ xfs_ialloc_inode_init(
        int                     version;
        int                     i, j;
        xfs_daddr_t             d;
+       xfs_ino_t               ino = 0;
 
        /*
         * Loop over the new block(s), filling in the inodes.
@@ -183,13 +186,29 @@ xfs_ialloc_inode_init(
        }
 
        /*
-        * Figure out what version number to use in the inodes we create.
-        * If the superblock version has caught up to the one that supports
-        * the new inode format, then use the new inode version.  Otherwise
-        * use the old version so that old kernels will continue to be
-        * able to use the file system.
+        * Figure out what version number to use in the inodes we create.  If
+        * the superblock version has caught up to the one that supports the new
+        * inode format, then use the new inode version.  Otherwise use the old
+        * version so that old kernels will continue to be able to use the file
+        * system.
+        *
+        * For v3 inodes, we also need to write the inode number into the inode,
+        * so calculate the first inode number of the chunk here as
+        * XFS_OFFBNO_TO_AGINO() only works within a filesystem block, not
+        * across multiple filesystem blocks (such as a cluster) and so cannot
+        * be used in the cluster buffer loop below.
+        *
+        * Further, because we are writing the inode directly into the buffer
+        * and calculating a CRC on the entire inode, we have ot log the entire
+        * inode so that the entire range the CRC covers is present in the log.
+        * That means for v3 inode we log the entire buffer rather than just the
+        * inode cores.
         */
-       if (xfs_sb_version_hasnlink(&mp->m_sb))
+       if (xfs_sb_version_hascrc(&mp->m_sb)) {
+               version = 3;
+               ino = XFS_AGINO_TO_INO(mp, agno,
+                                      XFS_OFFBNO_TO_AGINO(mp, agbno, 0));
+       } else if (xfs_sb_version_hasnlink(&mp->m_sb))
                version = 2;
        else
                version = 1;
@@ -212,17 +231,32 @@ xfs_ialloc_inode_init(
                 *      individual transactions causing a lot of log traffic.
                 */
                fbuf->b_ops = &xfs_inode_buf_ops;
-               xfs_buf_zero(fbuf, 0, ninodes << mp->m_sb.sb_inodelog);
+               xfs_buf_zero(fbuf, 0, BBTOB(fbuf->b_length));
                for (i = 0; i < ninodes; i++) {
                        int     ioffset = i << mp->m_sb.sb_inodelog;
-                       uint    isize = sizeof(struct xfs_dinode);
+                       uint    isize = xfs_dinode_size(version);
 
                        free = xfs_make_iptr(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);
+
+                       if (version == 3) {
+                               free->di_ino = cpu_to_be64(ino);
+                               ino++;
+                               uuid_copy(&free->di_uuid, &mp->m_sb.sb_uuid);
+                               xfs_dinode_calc_crc(mp, free);
+                       } else {
+                               /* just log the inode core */
+                               xfs_trans_log_buf(tp, fbuf, ioffset,
+                                                 ioffset + isize - 1);
+                       }
+               }
+               if (version == 3) {
+                       /* need to log the entire buffer */
+                       xfs_trans_log_buf(tp, fbuf, 0,
+                                         BBTOB(fbuf->b_length) - 1);
                }
                xfs_trans_inode_alloc_buf(tp, fbuf);
        }
@@ -369,7 +403,7 @@ xfs_ialloc_ag_alloc(
         * number from being easily guessable.
         */
        error = xfs_ialloc_inode_init(args.mp, tp, agno, args.agbno,
-                       args.len, random32());
+                       args.len, prandom_u32());
 
        if (error)
                return error;
@@ -1453,6 +1487,7 @@ xfs_ialloc_log_agi(
        /*
         * Log the allocation group inode header buffer.
         */
+       xfs_trans_buf_set_type(tp, bp, XFS_BLFT_AGI_BUF);
        xfs_trans_log_buf(tp, bp, first, last);
 }
 
@@ -1470,19 +1505,23 @@ xfs_check_agi_unlinked(
 #define xfs_check_agi_unlinked(agi)
 #endif
 
-static void
+static bool
 xfs_agi_verify(
        struct xfs_buf  *bp)
 {
        struct xfs_mount *mp = bp->b_target->bt_mount;
        struct xfs_agi  *agi = XFS_BUF_TO_AGI(bp);
-       int             agi_ok;
 
+       if (xfs_sb_version_hascrc(&mp->m_sb) &&
+           !uuid_equal(&agi->agi_uuid, &mp->m_sb.sb_uuid))
+                       return false;
        /*
         * Validate the magic number of the agi block.
         */
-       agi_ok = agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC) &&
-               XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum));
+       if (agi->agi_magicnum != cpu_to_be32(XFS_AGI_MAGIC))
+               return false;
+       if (!XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum)))
+               return false;
 
        /*
         * during growfs operations, the perag is not fully initialised,
@@ -1490,30 +1529,52 @@ xfs_agi_verify(
         * use it by using uncached buffers that don't have the perag attached
         * so we can detect and avoid this problem.
         */
-       if (bp->b_pag)
-               agi_ok = agi_ok && be32_to_cpu(agi->agi_seqno) ==
-                                               bp->b_pag->pag_agno;
+       if (bp->b_pag && be32_to_cpu(agi->agi_seqno) != bp->b_pag->pag_agno)
+               return false;
 
-       if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IALLOC_READ_AGI,
-                       XFS_RANDOM_IALLOC_READ_AGI))) {
-               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, agi);
-               xfs_buf_ioerror(bp, EFSCORRUPTED);
-       }
        xfs_check_agi_unlinked(agi);
+       return true;
 }
 
 static void
 xfs_agi_read_verify(
        struct xfs_buf  *bp)
 {
-       xfs_agi_verify(bp);
+       struct xfs_mount *mp = bp->b_target->bt_mount;
+       int             agi_ok = 1;
+
+       if (xfs_sb_version_hascrc(&mp->m_sb))
+               agi_ok = xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
+                                         offsetof(struct xfs_agi, agi_crc));
+       agi_ok = agi_ok && xfs_agi_verify(bp);
+
+       if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IALLOC_READ_AGI,
+                       XFS_RANDOM_IALLOC_READ_AGI))) {
+               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+               xfs_buf_ioerror(bp, EFSCORRUPTED);
+       }
 }
 
 static void
 xfs_agi_write_verify(
        struct xfs_buf  *bp)
 {
-       xfs_agi_verify(bp);
+       struct xfs_mount *mp = bp->b_target->bt_mount;
+       struct xfs_buf_log_item *bip = bp->b_fspriv;
+
+       if (!xfs_agi_verify(bp)) {
+               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+               xfs_buf_ioerror(bp, EFSCORRUPTED);
+               return;
+       }
+
+       if (!xfs_sb_version_hascrc(&mp->m_sb))
+               return;
+
+       if (bip)
+               XFS_BUF_TO_AGI(bp)->agi_lsn = cpu_to_be64(bip->bli_item.li_lsn);
+       xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length),
+                        offsetof(struct xfs_agi, agi_crc));
 }
 
 const struct xfs_buf_ops xfs_agi_buf_ops = {