diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index e983b58726792de59973310568698b6a7abfce6f..1c964def34fdef3b948a14479349b3086412d6e9 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -1078,7 +1078,7 @@ static int trunc_start(struct inode *inode, u64 newsize)
  * @mp: current metapath fully populated with buffers
  * @btotal: place to keep count of total blocks freed
  * @hgt: height we're processing
- * @first: true if this is the first call to this function for this height
+ * @keep_start: preserve the first meta pointer
  *
  * We sweep a metadata buffer (provided by the metapath) for blocks we need to
  * free, and free them all. However, we do it one rgrp at a time. If this
@@ -1094,7 +1094,7 @@ static int trunc_start(struct inode *inode, u64 newsize)
  */
 static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh,
 			      const struct metapath *mp, u32 *btotal, int hgt,
-			      bool preserve1)
+			      bool keep_start)
 {
 	struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
 	struct gfs2_rgrpd *rgd;
@@ -1119,7 +1119,7 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh,
 	top = metapointer(hgt, mp); /* first ptr from metapath */
 	/* If we're keeping some data at the truncation point, we've got to
 	   preserve the metadata tree by adding 1 to the starting metapath. */
-	if (preserve1)
+	if (keep_start)
 		top++;
 
 	bottom = (__be64 *)(bh->b_data + bh->b_size);
@@ -1286,9 +1286,9 @@ enum dealloc_states {
 	DEALLOC_DONE = 3,       /* process complete */
 };
 
-static bool mp_eq_to_hgt(struct metapath *mp, __u16 *nbof, unsigned int h)
+static bool mp_eq_to_hgt(struct metapath *mp, __u16 *list, unsigned int h)
 {
-	if (memcmp(mp->mp_list, nbof, h * sizeof(mp->mp_list[0])))
+	if (memcmp(mp->mp_list, list, h * sizeof(mp->mp_list[0])))
 		return false;
 	return true;
 }
@@ -1310,24 +1310,35 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
 	struct metapath mp;
 	struct buffer_head *dibh, *bh;
 	struct gfs2_holder rd_gh;
-	u64 lblock;
-	__u16 nbof[GFS2_MAX_META_HEIGHT]; /* new beginning of truncation */
+	unsigned int bsize_shift = sdp->sd_sb.sb_bsize_shift;
+	u64 lblock = (newsize + (1 << bsize_shift) - 1) >> bsize_shift;
+	__u16 start_list[GFS2_MAX_META_HEIGHT]; /* new beginning of truncation */
+	unsigned int start_aligned;
 	unsigned int strip_h = ip->i_height - 1;
 	u32 btotal = 0;
 	int ret, state;
 	int mp_h; /* metapath buffers are read in to this height */
 	u64 prev_bnr = 0;
-	bool preserve1; /* need to preserve the first meta pointer? */
-
-	if (!newsize)
-		lblock = 0;
-	else
-		lblock = (newsize - 1) >> sdp->sd_sb.sb_bsize_shift;
+	bool keep_start; /* need to preserve the first meta pointer? */
 
 	memset(&mp, 0, sizeof(mp));
 	find_metapath(sdp, lblock, &mp, ip->i_height);
 
-	memcpy(&nbof, &mp.mp_list, sizeof(nbof));
+	memcpy(start_list, mp.mp_list, sizeof(start_list));
+
+	/*
+	 * Set start_aligned to the metadata height up to which the truncate
+	 * point is aligned to the metadata tree (i.e., the truncate point is a
+	 * multiple of the granularity at the height above).  This determines
+	 * at which heights an additional meta pointer needs to be preserved:
+	 * an additional meta pointer is needed at a given height if
+	 * height < start_aligned.
+	 */
+	for (mp_h = ip->i_height - 1; mp_h > 0; mp_h--) {
+		if (start_list[mp_h])
+			break;
+	}
+	start_aligned = mp_h;
 
 	ret = gfs2_meta_inode_buffer(ip, &dibh);
 	if (ret)
@@ -1363,10 +1374,6 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
 		/* Truncate a full metapath at the given strip height.
 		 * Note that strip_h == mp_h in order to be in this state. */
 		case DEALLOC_MP_FULL:
-			/* If we're truncating to a non-zero size and the mp is
-			   at the beginning of file for the strip height, we
-			   need to preserve the first metadata pointer. */
-			preserve1 = (newsize && mp_eq_to_hgt(&mp, nbof, mp_h));
 			bh = mp.mp_bh[mp_h];
 			gfs2_assert_withdraw(sdp, bh);
 			if (gfs2_assert_withdraw(sdp,
@@ -1378,8 +1385,12 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
 				       prev_bnr, ip->i_height, strip_h, mp_h);
 			}
 			prev_bnr = bh->b_blocknr;
+
+			keep_start = mp_h < start_aligned &&
+				     mp_eq_to_hgt(&mp, start_list, mp_h);
+
 			ret = sweep_bh_for_rgrps(ip, &rd_gh, &mp, &btotal,
-						 mp_h, preserve1);
+						 mp_h, keep_start);
 			/* If we hit an error or just swept dinode buffer,
 			   just exit. */
 			if (ret || !mp_h) {
@@ -1403,7 +1414,7 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
 			   stripping the previous level of metadata. */
 			if (mp_h == 0) {
 				strip_h--;
-				memcpy(&mp.mp_list, &nbof, sizeof(nbof));
+				memcpy(mp.mp_list, start_list, sizeof(start_list));
 				mp_h = strip_h;
 				state = DEALLOC_FILL_MP;
 				break;