Browse Source

Merge tag 'xfs-iomap-for-linus-4.8-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs

Pull xfs and iomap fixes from Dave Chinner:
 "Most of these changes are small regression fixes that address problems
  introduced in the 4.8-rc1 window.  The two fixes that aren't (IO
  completion fix and superblock inprogress check) are fixes for problems
  introduced some time ago and need to be pushed back to stable kernels.

  Changes in this update:
   - iomap FIEMAP_EXTENT_MERGED usage fix
   - additional mount-time feature restrictions
   - rmap btree query fixes
   - freeze/unmount io completion workqueue fix
   - memory corruption fix for deferred operations handling"

* tag 'xfs-iomap-for-linus-4.8-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs:
  xfs: track log done items directly in the deferred pending work item
  iomap: don't set FIEMAP_EXTENT_MERGED for extent based filesystems
  xfs: prevent dropping ioend completions during buftarg wait
  xfs: fix superblock inprogress check
  xfs: simple btree query range should look right if LE lookup fails
  xfs: fix some key handling problems in _btree_simple_query_range
  xfs: don't log the entire end of the AGF
  xfs: disallow mounting of realtime + rmap filesystems
  xfs: don't perform lookups on zero-height btrees
Linus Torvalds 9 years ago
parent
commit
7d1ce606a3

+ 4 - 1
fs/iomap.c

@@ -428,9 +428,12 @@ static int iomap_to_fiemap(struct fiemap_extent_info *fi,
 		break;
 		break;
 	}
 	}
 
 
+	if (iomap->flags & IOMAP_F_MERGED)
+		flags |= FIEMAP_EXTENT_MERGED;
+
 	return fiemap_fill_next_extent(fi, iomap->offset,
 	return fiemap_fill_next_extent(fi, iomap->offset,
 			iomap->blkno != IOMAP_NULL_BLOCK ? iomap->blkno << 9: 0,
 			iomap->blkno != IOMAP_NULL_BLOCK ? iomap->blkno << 9: 0,
-			iomap->length, flags | FIEMAP_EXTENT_MERGED);
+			iomap->length, flags);
 
 
 }
 }
 
 

+ 2 - 0
fs/xfs/libxfs/xfs_alloc.c

@@ -2278,6 +2278,8 @@ xfs_alloc_log_agf(
 		offsetof(xfs_agf_t, agf_btreeblks),
 		offsetof(xfs_agf_t, agf_btreeblks),
 		offsetof(xfs_agf_t, agf_uuid),
 		offsetof(xfs_agf_t, agf_uuid),
 		offsetof(xfs_agf_t, agf_rmap_blocks),
 		offsetof(xfs_agf_t, agf_rmap_blocks),
+		/* needed so that we don't log the whole rest of the structure: */
+		offsetof(xfs_agf_t, agf_spare64),
 		sizeof(xfs_agf_t)
 		sizeof(xfs_agf_t)
 	};
 	};
 
 

+ 13 - 1
fs/xfs/libxfs/xfs_btree.c

@@ -1814,6 +1814,10 @@ xfs_btree_lookup(
 
 
 	XFS_BTREE_STATS_INC(cur, lookup);
 	XFS_BTREE_STATS_INC(cur, lookup);
 
 
+	/* No such thing as a zero-level tree. */
+	if (cur->bc_nlevels == 0)
+		return -EFSCORRUPTED;
+
 	block = NULL;
 	block = NULL;
 	keyno = 0;
 	keyno = 0;
 
 
@@ -4554,15 +4558,22 @@ xfs_btree_simple_query_range(
 	if (error)
 	if (error)
 		goto out;
 		goto out;
 
 
+	/* Nothing?  See if there's anything to the right. */
+	if (!stat) {
+		error = xfs_btree_increment(cur, 0, &stat);
+		if (error)
+			goto out;
+	}
+
 	while (stat) {
 	while (stat) {
 		/* Find the record. */
 		/* Find the record. */
 		error = xfs_btree_get_rec(cur, &recp, &stat);
 		error = xfs_btree_get_rec(cur, &recp, &stat);
 		if (error || !stat)
 		if (error || !stat)
 			break;
 			break;
-		cur->bc_ops->init_high_key_from_rec(&rec_key, recp);
 
 
 		/* Skip if high_key(rec) < low_key. */
 		/* Skip if high_key(rec) < low_key. */
 		if (firstrec) {
 		if (firstrec) {
+			cur->bc_ops->init_high_key_from_rec(&rec_key, recp);
 			firstrec = false;
 			firstrec = false;
 			diff = cur->bc_ops->diff_two_keys(cur, low_key,
 			diff = cur->bc_ops->diff_two_keys(cur, low_key,
 					&rec_key);
 					&rec_key);
@@ -4571,6 +4582,7 @@ xfs_btree_simple_query_range(
 		}
 		}
 
 
 		/* Stop if high_key < low_key(rec). */
 		/* Stop if high_key < low_key(rec). */
+		cur->bc_ops->init_key_from_rec(&rec_key, recp);
 		diff = cur->bc_ops->diff_two_keys(cur, &rec_key, high_key);
 		diff = cur->bc_ops->diff_two_keys(cur, &rec_key, high_key);
 		if (diff > 0)
 		if (diff > 0)
 			break;
 			break;

+ 4 - 13
fs/xfs/libxfs/xfs_defer.c

@@ -194,7 +194,7 @@ xfs_defer_trans_abort(
 	/* Abort intent items. */
 	/* Abort intent items. */
 	list_for_each_entry(dfp, &dop->dop_pending, dfp_list) {
 	list_for_each_entry(dfp, &dop->dop_pending, dfp_list) {
 		trace_xfs_defer_pending_abort(tp->t_mountp, dfp);
 		trace_xfs_defer_pending_abort(tp->t_mountp, dfp);
-		if (dfp->dfp_committed)
+		if (!dfp->dfp_done)
 			dfp->dfp_type->abort_intent(dfp->dfp_intent);
 			dfp->dfp_type->abort_intent(dfp->dfp_intent);
 	}
 	}
 
 
@@ -290,7 +290,6 @@ xfs_defer_finish(
 	struct xfs_defer_pending	*dfp;
 	struct xfs_defer_pending	*dfp;
 	struct list_head		*li;
 	struct list_head		*li;
 	struct list_head		*n;
 	struct list_head		*n;
-	void				*done_item = NULL;
 	void				*state;
 	void				*state;
 	int				error = 0;
 	int				error = 0;
 	void				(*cleanup_fn)(struct xfs_trans *, void *, int);
 	void				(*cleanup_fn)(struct xfs_trans *, void *, int);
@@ -309,19 +308,11 @@ xfs_defer_finish(
 		if (error)
 		if (error)
 			goto out;
 			goto out;
 
 
-		/* Mark all pending intents as committed. */
-		list_for_each_entry_reverse(dfp, &dop->dop_pending, dfp_list) {
-			if (dfp->dfp_committed)
-				break;
-			trace_xfs_defer_pending_commit((*tp)->t_mountp, dfp);
-			dfp->dfp_committed = true;
-		}
-
 		/* Log an intent-done item for the first pending item. */
 		/* Log an intent-done item for the first pending item. */
 		dfp = list_first_entry(&dop->dop_pending,
 		dfp = list_first_entry(&dop->dop_pending,
 				struct xfs_defer_pending, dfp_list);
 				struct xfs_defer_pending, dfp_list);
 		trace_xfs_defer_pending_finish((*tp)->t_mountp, dfp);
 		trace_xfs_defer_pending_finish((*tp)->t_mountp, dfp);
-		done_item = dfp->dfp_type->create_done(*tp, dfp->dfp_intent,
+		dfp->dfp_done = dfp->dfp_type->create_done(*tp, dfp->dfp_intent,
 				dfp->dfp_count);
 				dfp->dfp_count);
 		cleanup_fn = dfp->dfp_type->finish_cleanup;
 		cleanup_fn = dfp->dfp_type->finish_cleanup;
 
 
@@ -331,7 +322,7 @@ xfs_defer_finish(
 			list_del(li);
 			list_del(li);
 			dfp->dfp_count--;
 			dfp->dfp_count--;
 			error = dfp->dfp_type->finish_item(*tp, dop, li,
 			error = dfp->dfp_type->finish_item(*tp, dop, li,
-					done_item, &state);
+					dfp->dfp_done, &state);
 			if (error) {
 			if (error) {
 				/*
 				/*
 				 * Clean up after ourselves and jump out.
 				 * Clean up after ourselves and jump out.
@@ -428,8 +419,8 @@ xfs_defer_add(
 		dfp = kmem_alloc(sizeof(struct xfs_defer_pending),
 		dfp = kmem_alloc(sizeof(struct xfs_defer_pending),
 				KM_SLEEP | KM_NOFS);
 				KM_SLEEP | KM_NOFS);
 		dfp->dfp_type = defer_op_types[type];
 		dfp->dfp_type = defer_op_types[type];
-		dfp->dfp_committed = false;
 		dfp->dfp_intent = NULL;
 		dfp->dfp_intent = NULL;
+		dfp->dfp_done = NULL;
 		dfp->dfp_count = 0;
 		dfp->dfp_count = 0;
 		INIT_LIST_HEAD(&dfp->dfp_work);
 		INIT_LIST_HEAD(&dfp->dfp_work);
 		list_add_tail(&dfp->dfp_list, &dop->dop_intake);
 		list_add_tail(&dfp->dfp_list, &dop->dop_intake);

+ 1 - 1
fs/xfs/libxfs/xfs_defer.h

@@ -30,8 +30,8 @@ struct xfs_defer_op_type;
 struct xfs_defer_pending {
 struct xfs_defer_pending {
 	const struct xfs_defer_op_type	*dfp_type;	/* function pointers */
 	const struct xfs_defer_op_type	*dfp_type;	/* function pointers */
 	struct list_head		dfp_list;	/* pending items */
 	struct list_head		dfp_list;	/* pending items */
-	bool				dfp_committed;	/* committed trans? */
 	void				*dfp_intent;	/* log intent item */
 	void				*dfp_intent;	/* log intent item */
+	void				*dfp_done;	/* log done item */
 	struct list_head		dfp_work;	/* work items */
 	struct list_head		dfp_work;	/* work items */
 	unsigned int			dfp_count;	/* # extent items */
 	unsigned int			dfp_count;	/* # extent items */
 };
 };

+ 4 - 2
fs/xfs/libxfs/xfs_format.h

@@ -674,7 +674,8 @@ typedef struct xfs_agf {
 #define	XFS_AGF_BTREEBLKS	0x00000800
 #define	XFS_AGF_BTREEBLKS	0x00000800
 #define	XFS_AGF_UUID		0x00001000
 #define	XFS_AGF_UUID		0x00001000
 #define	XFS_AGF_RMAP_BLOCKS	0x00002000
 #define	XFS_AGF_RMAP_BLOCKS	0x00002000
-#define	XFS_AGF_NUM_BITS	14
+#define	XFS_AGF_SPARE64		0x00004000
+#define	XFS_AGF_NUM_BITS	15
 #define	XFS_AGF_ALL_BITS	((1 << XFS_AGF_NUM_BITS) - 1)
 #define	XFS_AGF_ALL_BITS	((1 << XFS_AGF_NUM_BITS) - 1)
 
 
 #define XFS_AGF_FLAGS \
 #define XFS_AGF_FLAGS \
@@ -691,7 +692,8 @@ typedef struct xfs_agf {
 	{ XFS_AGF_LONGEST,	"LONGEST" }, \
 	{ XFS_AGF_LONGEST,	"LONGEST" }, \
 	{ XFS_AGF_BTREEBLKS,	"BTREEBLKS" }, \
 	{ XFS_AGF_BTREEBLKS,	"BTREEBLKS" }, \
 	{ XFS_AGF_UUID,		"UUID" }, \
 	{ XFS_AGF_UUID,		"UUID" }, \
-	{ XFS_AGF_RMAP_BLOCKS,	"RMAP_BLOCKS" }
+	{ XFS_AGF_RMAP_BLOCKS,	"RMAP_BLOCKS" }, \
+	{ XFS_AGF_SPARE64,	"SPARE64" }
 
 
 /* disk block (xfs_daddr_t) in the AG */
 /* disk block (xfs_daddr_t) in the AG */
 #define XFS_AGF_DADDR(mp)	((xfs_daddr_t)(1 << (mp)->m_sectbb_log))
 #define XFS_AGF_DADDR(mp)	((xfs_daddr_t)(1 << (mp)->m_sectbb_log))

+ 2 - 1
fs/xfs/libxfs/xfs_sb.c

@@ -583,7 +583,8 @@ xfs_sb_verify(
 	 * Only check the in progress field for the primary superblock as
 	 * Only check the in progress field for the primary superblock as
 	 * mkfs.xfs doesn't clear it from secondary superblocks.
 	 * mkfs.xfs doesn't clear it from secondary superblocks.
 	 */
 	 */
-	return xfs_mount_validate_sb(mp, &sb, bp->b_bn == XFS_SB_DADDR,
+	return xfs_mount_validate_sb(mp, &sb,
+				     bp->b_maps[0].bm_bn == XFS_SB_DADDR,
 				     check_version);
 				     check_version);
 }
 }
 
 

+ 1 - 1
fs/xfs/xfs_buf.c

@@ -1611,7 +1611,7 @@ xfs_wait_buftarg(
 	 */
 	 */
 	while (percpu_counter_sum(&btp->bt_io_count))
 	while (percpu_counter_sum(&btp->bt_io_count))
 		delay(100);
 		delay(100);
-	drain_workqueue(btp->bt_mount->m_buf_workqueue);
+	flush_workqueue(btp->bt_mount->m_buf_workqueue);
 
 
 	/* loop until there is nothing left on the lru list. */
 	/* loop until there is nothing left on the lru list. */
 	while (list_lru_count(&btp->bt_lru)) {
 	while (list_lru_count(&btp->bt_lru)) {

+ 8 - 1
fs/xfs/xfs_super.c

@@ -1574,9 +1574,16 @@ xfs_fs_fill_super(
 		}
 		}
 	}
 	}
 
 
-	if (xfs_sb_version_hasrmapbt(&mp->m_sb))
+	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+		if (mp->m_sb.sb_rblocks) {
+			xfs_alert(mp,
+	"EXPERIMENTAL reverse mapping btree not compatible with realtime device!");
+			error = -EINVAL;
+			goto out_filestream_unmount;
+		}
 		xfs_alert(mp,
 		xfs_alert(mp,
 	"EXPERIMENTAL reverse mapping btree feature enabled. Use at your own risk!");
 	"EXPERIMENTAL reverse mapping btree feature enabled. Use at your own risk!");
+	}
 
 
 	error = xfs_mountfs(mp);
 	error = xfs_mountfs(mp);
 	if (error)
 	if (error)

+ 1 - 1
fs/xfs/xfs_trace.h

@@ -2295,7 +2295,7 @@ DECLARE_EVENT_CLASS(xfs_defer_pending_class,
 		__entry->dev = mp ? mp->m_super->s_dev : 0;
 		__entry->dev = mp ? mp->m_super->s_dev : 0;
 		__entry->type = dfp->dfp_type->type;
 		__entry->type = dfp->dfp_type->type;
 		__entry->intent = dfp->dfp_intent;
 		__entry->intent = dfp->dfp_intent;
-		__entry->committed = dfp->dfp_committed;
+		__entry->committed = dfp->dfp_done != NULL;
 		__entry->nr = dfp->dfp_count;
 		__entry->nr = dfp->dfp_count;
 	),
 	),
 	TP_printk("dev %d:%d optype %d intent %p committed %d nr %d\n",
 	TP_printk("dev %d:%d optype %d intent %p committed %d nr %d\n",

+ 7 - 1
include/linux/iomap.h

@@ -18,6 +18,11 @@ struct vm_fault;
 #define IOMAP_MAPPED	0x03	/* blocks allocated @blkno */
 #define IOMAP_MAPPED	0x03	/* blocks allocated @blkno */
 #define IOMAP_UNWRITTEN	0x04	/* blocks allocated @blkno in unwritten state */
 #define IOMAP_UNWRITTEN	0x04	/* blocks allocated @blkno in unwritten state */
 
 
+/*
+ * Flags for iomap mappings:
+ */
+#define IOMAP_F_MERGED	0x01	/* contains multiple blocks/extents */
+
 /*
 /*
  * Magic value for blkno:
  * Magic value for blkno:
  */
  */
@@ -27,7 +32,8 @@ struct iomap {
 	sector_t		blkno;	/* 1st sector of mapping, 512b units */
 	sector_t		blkno;	/* 1st sector of mapping, 512b units */
 	loff_t			offset;	/* file offset of mapping, bytes */
 	loff_t			offset;	/* file offset of mapping, bytes */
 	u64			length;	/* length of mapping, bytes */
 	u64			length;	/* length of mapping, bytes */
-	int			type;	/* type of mapping */
+	u16			type;	/* type of mapping */
+	u16			flags;	/* flags for mapping */
 	struct block_device	*bdev;	/* block device for I/O */
 	struct block_device	*bdev;	/* block device for I/O */
 };
 };