extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
                                struct xfs_name *name, xfs_ino_t ino,
                                xfs_extlen_t tot);
+extern bool xfs_dir2_sf_replace_needblock(struct xfs_inode *dp,
+                               xfs_ino_t inum);
 extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
                                struct xfs_name *name, xfs_ino_t inum,
                                xfs_extlen_t tot);
 
        return 0;
 }
 
+/*
+ * Check whether the sf dir replace operation need more blocks.
+ */
+bool
+xfs_dir2_sf_replace_needblock(
+       struct xfs_inode        *dp,
+       xfs_ino_t               inum)
+{
+       int                     newsize;
+       struct xfs_dir2_sf_hdr  *sfp;
+
+       if (dp->i_d.di_format != XFS_DINODE_FMT_LOCAL)
+               return false;
+
+       sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data;
+       newsize = dp->i_df.if_bytes + (sfp->count + 1) * XFS_INO64_DIFF;
+
+       return inum > XFS_DIR2_MAX_SHORT_INUM &&
+              sfp->i8count == 0 && newsize > XFS_IFORK_DSIZE(dp);
+}
+
 /*
  * Replace the inode number of an entry in a shortform directory.
  */
         */
        if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) {
                int     error;                  /* error return value */
-               int     newsize;                /* new inode size */
 
-               newsize = dp->i_df.if_bytes + (sfp->count + 1) * XFS_INO64_DIFF;
                /*
                 * Won't fit as shortform, convert to block then do replace.
                 */
-               if (newsize > XFS_IFORK_DSIZE(dp)) {
+               if (xfs_dir2_sf_replace_needblock(dp, args->inumber)) {
                        error = xfs_dir2_sf_to_block(args);
-                       if (error) {
+                       if (error)
                                return error;
-                       }
                        return xfs_dir2_block_replace(args);
                }
                /*
 
        struct xfs_trans        *tp;
        struct xfs_inode        *wip = NULL;            /* whiteout inode */
        struct xfs_inode        *inodes[__XFS_SORT_INODES];
+       struct xfs_buf          *agibp;
        int                     num_inodes = __XFS_SORT_INODES;
        bool                    new_parent = (src_dp != target_dp);
        bool                    src_is_directory = S_ISDIR(VFS_I(src_ip)->i_mode);
                 * In case there is already an entry with the same
                 * name at the destination directory, remove it first.
                 */
+
+               /*
+                * Check whether the replace operation will need to allocate
+                * blocks.  This happens when the shortform directory lacks
+                * space and we have to convert it to a block format directory.
+                * When more blocks are necessary, we must lock the AGI first
+                * to preserve locking order (AGI -> AGF).
+                */
+               if (xfs_dir2_sf_replace_needblock(target_dp, src_ip->i_ino)) {
+                       error = xfs_read_agi(mp, tp,
+                                       XFS_INO_TO_AGNO(mp, target_ip->i_ino),
+                                       &agibp);
+                       if (error)
+                               goto out_trans_cancel;
+               }
+
                error = xfs_dir_replace(tp, target_dp, target_name,
                                        src_ip->i_ino, spaceres);
                if (error)