struct ocfs2_extent_block *eb =
                (struct ocfs2_extent_block *)bh->b_data;
 
+       mlog(0, "Validating extent block %llu\n",
+            (unsigned long long)bh->b_blocknr);
+
        if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) {
                ocfs2_error(sb,
                            "Extent block #%llu has bad signature %.*s",
        int rc;
        struct buffer_head *tmp = *bh;
 
-       rc = ocfs2_read_block(inode, eb_blkno, &tmp);
-       if (rc)
-               goto out;
-
-       rc = ocfs2_validate_extent_block(inode->i_sb, tmp);
-       if (rc) {
-               brelse(tmp);
-               goto out;
-       }
+       rc = ocfs2_read_block(inode, eb_blkno, &tmp,
+                             ocfs2_validate_extent_block);
 
        /* If ocfs2_read_block() got us a new bh, pass it up. */
-       if (!*bh)
+       if (!rc && !*bh)
                *bh = tmp;
 
-out:
        return rc;
 }
 
 
 
 #include "buffer_head_io.h"
 
+/*
+ * Bits on bh->b_state used by ocfs2.
+ *
+ * These MUST be after the JBD2 bits.  Currently BH_Unshadow is the last
+ * JBD2 bit.
+ */
+enum ocfs2_state_bits {
+       BH_NeedsValidate = BH_Unshadow + 1,
+};
+
+/* Expand the magic b_state functions */
+BUFFER_FNS(NeedsValidate, needs_validate);
+
 int ocfs2_write_block(struct ocfs2_super *osb, struct buffer_head *bh,
                      struct inode *inode)
 {
 }
 
 int ocfs2_read_blocks(struct inode *inode, u64 block, int nr,
-                     struct buffer_head *bhs[], int flags)
+                     struct buffer_head *bhs[], int flags,
+                     int (*validate)(struct super_block *sb,
+                                     struct buffer_head *bh))
 {
        int status = 0;
        int i, ignore_cache = 0;
 
                        clear_buffer_uptodate(bh);
                        get_bh(bh); /* for end_buffer_read_sync() */
+                       if (validate)
+                               set_buffer_needs_validate(bh);
                        bh->b_end_io = end_buffer_read_sync;
                        submit_bh(READ, bh);
                        continue;
                                bhs[i] = NULL;
                                continue;
                        }
+
+                       if (buffer_needs_validate(bh)) {
+                               /* We never set NeedsValidate if the
+                                * buffer was held by the journal, so
+                                * that better not have changed */
+                               BUG_ON(buffer_jbd(bh));
+                               clear_buffer_needs_validate(bh);
+                               status = validate(inode->i_sb, bh);
+                               if (status) {
+                                       put_bh(bh);
+                                       bhs[i] = NULL;
+                                       continue;
+                               }
+                       }
                }
 
                /* Always set the buffer in the cache, even if it was
 
 void ocfs2_end_buffer_io_sync(struct buffer_head *bh,
                             int uptodate);
 
-static inline int ocfs2_read_block(struct inode               *inode,
-                                  u64                  off,
-                                  struct buffer_head **bh);
-
 int ocfs2_write_block(struct ocfs2_super          *osb,
                      struct buffer_head  *bh,
                      struct inode        *inode);
-int ocfs2_read_blocks(struct inode       *inode,
-                     u64                  block,
-                     int                  nr,
-                     struct buffer_head  *bhs[],
-                     int                  flags);
 int ocfs2_read_blocks_sync(struct ocfs2_super *osb, u64 block,
                           unsigned int nr, struct buffer_head *bhs[]);
 
+/*
+ * If not NULL, validate() will be called on a buffer that is freshly
+ * read from disk.  It will not be called if the buffer was in cache.
+ * Note that if validate() is being used for this buffer, it needs to
+ * be set even for a READAHEAD call, as it marks the buffer for later
+ * validation.
+ */
+int ocfs2_read_blocks(struct inode *inode, u64 block, int nr,
+                     struct buffer_head *bhs[], int flags,
+                     int (*validate)(struct super_block *sb,
+                                     struct buffer_head *bh));
+
 int ocfs2_write_super_or_backup(struct ocfs2_super *osb,
                                struct buffer_head *bh);
 
 #define OCFS2_BH_READAHEAD         8
 
 static inline int ocfs2_read_block(struct inode *inode, u64 off,
-                                  struct buffer_head **bh)
+                                  struct buffer_head **bh,
+                                  int (*validate)(struct super_block *sb,
+                                                  struct buffer_head *bh))
 {
        int status = 0;
 
                goto bail;
        }
 
-       status = ocfs2_read_blocks(inode, off, 1, bh, 0);
+       status = ocfs2_read_blocks(inode, off, 1, bh, 0, validate);
 
 bail:
        return status;
 
         * Nothing yet.  We don't validate dirents here, that's handled
         * in-place when the code walks them.
         */
+       mlog(0, "Validating dirblock %llu\n",
+            (unsigned long long)bh->b_blocknr);
 
        return 0;
 }
                goto out;
        }
 
-       rc = ocfs2_read_blocks(inode, p_blkno, 1, &tmp, flags);
+       rc = ocfs2_read_blocks(inode, p_blkno, 1, &tmp, flags,
+                              ocfs2_validate_dir_block);
        if (rc) {
                mlog_errno(rc);
                goto out;
        }
 
-       if (!(flags & OCFS2_BH_READAHEAD)) {
-               rc = ocfs2_validate_dir_block(inode->i_sb, tmp);
-               if (rc) {
-                       brelse(tmp);
-                       goto out;
-               }
-       }
-
        /* If ocfs2_read_blocks() got us a new bh, pass it up.  */
        if (!*bh)
                *bh = tmp;
 
        int rc = -EINVAL;
        struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data;
 
+       mlog(0, "Validating dinode %llu\n",
+            (unsigned long long)bh->b_blocknr);
+
        BUG_ON(!buffer_uptodate(bh));
 
        if (!OCFS2_IS_VALID_DINODE(di)) {
        struct buffer_head *tmp = *bh;
 
        rc = ocfs2_read_blocks(inode, OCFS2_I(inode)->ip_blkno, 1, &tmp,
-                              flags);
-       if (rc)
-               goto out;
-
-       if (!(flags & OCFS2_BH_READAHEAD)) {
-               rc = ocfs2_validate_inode_block(inode->i_sb, tmp);
-               if (rc) {
-                       brelse(tmp);
-                       goto out;
-               }
-       }
+                              flags, ocfs2_validate_inode_block);
 
        /* If ocfs2_read_blocks() got us a new bh, pass it up. */
-       if (!*bh)
+       if (!rc && !*bh)
                *bh = tmp;
 
-out:
        return rc;
 }
 
 
                (struct ocfs2_group_desc *)group_bh->b_data;
        u16 cl_bpc = le16_to_cpu(di->id2.i_chain.cl_bpc);
 
-       ret = ocfs2_validate_group_descriptor(inode->i_sb, di, group_bh, 1);
+       ret = ocfs2_check_group_descriptor(inode->i_sb, di, group_bh);
        if (ret)
                goto out;
 
 
         * this is not true, the read of -1 (UINT64_MAX) will fail.
         */
        ret = ocfs2_read_blocks(si->si_inode, -1, si->si_blocks, si->si_bh,
-                               OCFS2_BH_IGNORE_CACHE);
+                               OCFS2_BH_IGNORE_CACHE, NULL);
        if (ret == 0) {
                spin_lock(&osb->osb_lock);
                ocfs2_update_slot_info(si);
 
                bh = NULL;  /* Acquire a fresh bh */
                status = ocfs2_read_blocks(si->si_inode, blkno, 1, &bh,
-                                          OCFS2_BH_IGNORE_CACHE);
+                                          OCFS2_BH_IGNORE_CACHE, NULL);
                if (status < 0) {
                        mlog_errno(status);
                        goto bail;
 
        return (u32)le16_to_cpu(cl->cl_cpg) * (u32)le16_to_cpu(cl->cl_bpc);
 }
 
-int ocfs2_validate_group_descriptor(struct super_block *sb,
-                                   struct ocfs2_dinode *di,
-                                   struct buffer_head *bh,
-                                   int clean_error)
-{
-       unsigned int max_bits;
-       struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *)bh->b_data;
-
 #define do_error(fmt, ...)                                             \
        do{                                                             \
                if (clean_error)                                        \
                        ocfs2_error(sb, fmt, ##__VA_ARGS__);            \
        } while (0)
 
+static int ocfs2_validate_gd_self(struct super_block *sb,
+                                 struct buffer_head *bh,
+                                 int clean_error)
+{
+       struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *)bh->b_data;
+
        if (!OCFS2_IS_VALID_GROUP_DESC(gd)) {
                do_error("Group descriptor #%llu has bad signature %.*s",
                         (unsigned long long)bh->b_blocknr, 7,
                return -EINVAL;
        }
 
+       if (le16_to_cpu(gd->bg_free_bits_count) > le16_to_cpu(gd->bg_bits)) {
+               do_error("Group descriptor #%llu has bit count %u but "
+                        "claims that %u are free",
+                        (unsigned long long)bh->b_blocknr,
+                        le16_to_cpu(gd->bg_bits),
+                        le16_to_cpu(gd->bg_free_bits_count));
+               return -EINVAL;
+       }
+
+       if (le16_to_cpu(gd->bg_bits) > (8 * le16_to_cpu(gd->bg_size))) {
+               do_error("Group descriptor #%llu has bit count %u but "
+                        "max bitmap bits of %u",
+                        (unsigned long long)bh->b_blocknr,
+                        le16_to_cpu(gd->bg_bits),
+                        8 * le16_to_cpu(gd->bg_size));
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ocfs2_validate_gd_parent(struct super_block *sb,
+                                   struct ocfs2_dinode *di,
+                                   struct buffer_head *bh,
+                                   int clean_error)
+{
+       unsigned int max_bits;
+       struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *)bh->b_data;
+
        if (di->i_blkno != gd->bg_parent_dinode) {
                do_error("Group descriptor #%llu has bad parent "
                         "pointer (%llu, expected %llu)",
                return -EINVAL;
        }
 
-       if (le16_to_cpu(gd->bg_free_bits_count) > le16_to_cpu(gd->bg_bits)) {
-               do_error("Group descriptor #%llu has bit count %u but "
-                        "claims that %u are free",
-                        (unsigned long long)bh->b_blocknr,
-                        le16_to_cpu(gd->bg_bits),
-                        le16_to_cpu(gd->bg_free_bits_count));
-               return -EINVAL;
-       }
+       return 0;
+}
 
-       if (le16_to_cpu(gd->bg_bits) > (8 * le16_to_cpu(gd->bg_size))) {
-               do_error("Group descriptor #%llu has bit count %u but "
-                        "max bitmap bits of %u",
-                        (unsigned long long)bh->b_blocknr,
-                        le16_to_cpu(gd->bg_bits),
-                        8 * le16_to_cpu(gd->bg_size));
-               return -EINVAL;
-       }
 #undef do_error
 
-       return 0;
+/*
+ * This version only prints errors.  It does not fail the filesystem, and
+ * exists only for resize.
+ */
+int ocfs2_check_group_descriptor(struct super_block *sb,
+                                struct ocfs2_dinode *di,
+                                struct buffer_head *bh)
+{
+       int rc;
+
+       rc = ocfs2_validate_gd_self(sb, bh, 1);
+       if (!rc)
+               rc = ocfs2_validate_gd_parent(sb, di, bh, 1);
+
+       return rc;
+}
+
+static int ocfs2_validate_group_descriptor(struct super_block *sb,
+                                          struct buffer_head *bh)
+{
+       mlog(0, "Validating group descriptor %llu\n",
+            (unsigned long long)bh->b_blocknr);
+
+       return ocfs2_validate_gd_self(sb, bh, 0);
 }
 
 int ocfs2_read_group_descriptor(struct inode *inode, struct ocfs2_dinode *di,
        int rc;
        struct buffer_head *tmp = *bh;
 
-       rc = ocfs2_read_block(inode, gd_blkno, &tmp);
+       rc = ocfs2_read_block(inode, gd_blkno, &tmp,
+                             ocfs2_validate_group_descriptor);
        if (rc)
                goto out;
 
-       rc = ocfs2_validate_group_descriptor(inode->i_sb, di, tmp, 0);
+       rc = ocfs2_validate_gd_parent(inode->i_sb, di, tmp, 0);
        if (rc) {
                brelse(tmp);
                goto out;
 
 u64 ocfs2_which_cluster_group(struct inode *inode, u32 cluster);
 
 /*
- * By default, ocfs2_validate_group_descriptor() calls ocfs2_error() when it
+ * By default, ocfs2_read_group_descriptor() calls ocfs2_error() when it
  * finds a problem.  A caller that wants to check a group descriptor
- * without going readonly passes a nonzero clean_error.  This is only
- * resize, really.  Everyone else should be using
- * ocfs2_read_group_descriptor().
+ * without going readonly should read the block with ocfs2_read_block[s]()
+ * and then checking it with this function.  This is only resize, really.
+ * Everyone else should be using ocfs2_read_group_descriptor().
  */
-int ocfs2_validate_group_descriptor(struct super_block *sb,
-                                   struct ocfs2_dinode *di,
-                                   struct buffer_head *bh,
-                                   int clean_error);
+int ocfs2_check_group_descriptor(struct super_block *sb,
+                                struct ocfs2_dinode *di,
+                                struct buffer_head *bh);
 /*
  * Read a group descriptor block into *bh.  If *bh is NULL, a bh will be
  * allocated.  This is a cached read.  The descriptor will be validated with
 
        int rc;
 
        rc = ocfs2_read_blocks(bucket->bu_inode, xb_blkno,
-                              bucket->bu_blocks, bucket->bu_bhs, 0);
+                              bucket->bu_blocks, bucket->bu_bhs, 0,
+                              NULL);
        if (rc)
                ocfs2_xattr_bucket_relse(bucket);
        return rc;
        int rc;
        struct buffer_head *tmp = *bh;
 
-       rc = ocfs2_read_block(inode, xb_blkno, &tmp);
-       if (!rc) {
-               rc = ocfs2_validate_xattr_block(inode->i_sb, tmp);
-               if (rc)
-                       brelse(tmp);
-       }
+       rc = ocfs2_read_block(inode, xb_blkno, &tmp,
+                             ocfs2_validate_xattr_block);
 
        /* If ocfs2_read_block() got us a new bh, pass it up. */
        if (!rc && !*bh)
                blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster);
                /* Copy ocfs2_xattr_value */
                for (i = 0; i < num_clusters * bpc; i++, blkno++) {
-                       ret = ocfs2_read_block(inode, blkno, &bh);
+                       ret = ocfs2_read_block(inode, blkno, &bh, NULL);
                        if (ret) {
                                mlog_errno(ret);
                                goto out;
                blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster);
 
                for (i = 0; i < num_clusters * bpc; i++, blkno++) {
-                       ret = ocfs2_read_block(inode, blkno, &bh);
+                       ret = ocfs2_read_block(inode, blkno, &bh, NULL);
                        if (ret) {
                                mlog_errno(ret);
                                goto out;
                base = xis->base;
                credits += OCFS2_INODE_UPDATE_CREDITS;
        } else {
-               int i, block_off;
+               int i, block_off = 0;
                xb = (struct ocfs2_xattr_block *)xbs->xattr_bh->b_data;
                xe = xbs->here;
                name_offset = le16_to_cpu(xe->xe_name_offset);
                        break;
                }
 
+
                xe_name = bucket_block(bucket, block_off) + new_offset;
                if (!memcmp(name, xe_name, name_len)) {
                        *xe_index = i;
                        goto out;
                }
 
-               ret = ocfs2_read_block(inode, prev_blkno, &old_bh);
+               ret = ocfs2_read_block(inode, prev_blkno, &old_bh, NULL);
                if (ret < 0) {
                        mlog_errno(ret);
                        brelse(new_bh);
        ocfs2_journal_dirty(handle, first_bh);
 
        /* update the new bucket header. */
-       ret = ocfs2_read_block(inode, to_blk_start, &bh);
+       ret = ocfs2_read_block(inode, to_blk_start, &bh, NULL);
        if (ret < 0) {
                mlog_errno(ret);
                goto out;
                goto out;
        }
 
-       ret = ocfs2_read_block(inode, p_blkno, &first_bh);
+       ret = ocfs2_read_block(inode, p_blkno, &first_bh, NULL);
        if (ret) {
                mlog_errno(ret);
                goto out;
        BUG_ON(value_blk != (offset + OCFS2_XATTR_ROOT_SIZE - 1) / blocksize);
        value_blk += header_bh->b_blocknr;
 
-       ret = ocfs2_read_block(inode, value_blk, &value_bh);
+       ret = ocfs2_read_block(inode, value_blk, &value_bh, NULL);
        if (ret) {
                mlog_errno(ret);
                goto out;