[PATCH 2/2] xfs: implement FIEMAP_FLAG_FREESPACE_*

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Dave Chinner <dchinner@xxxxxxxxxx>

As you wish.

Signed-off-by: Dave Chinner <dchinner@xxxxxxxxxx>
---
 fs/xfs/xfs_alloc.c |  219 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_alloc.h |    7 ++
 fs/xfs/xfs_iops.c  |   12 ++-
 3 files changed, 237 insertions(+), 1 deletion(-)

diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c
index 335206a..ee680c9 100644
--- a/fs/xfs/xfs_alloc.c
+++ b/fs/xfs/xfs_alloc.c
@@ -2470,3 +2470,222 @@ error0:
 	xfs_perag_put(args.pag);
 	return error;
 }
+
+/*
+ * Walk the extents in the tree given by the cursor, and dump them all into the
+ * fieinfo. At the last extent in the tree, set the FIEMAP_EXTENT_LAST flag so
+ * that we return only free space from this tree in a given request.
+ */
+static int
+xfs_alloc_ag_freespace_map(
+	struct xfs_btree_cur    *cur,
+	struct fiemap_extent_info *fieinfo,
+	xfs_agblock_t		sagbno,
+	xfs_agblock_t		eagbno)
+{
+	int	error = 0;
+	int	i = 1;
+
+	/*
+	 * Loop until we have either filled the fiemap or reached the end of
+	 * the AG walk.
+	 */
+	while (i) {
+		xfs_agblock_t	fbno;
+		xfs_extlen_t	flen;
+		xfs_daddr_t	dbno;
+		xfs_fileoff_t	dlen;
+		int		flags = 0;
+
+		error = xfs_alloc_get_rec(cur, &fbno, &flen, &i);
+		if (error)
+			break;
+		XFS_WANT_CORRUPTED_RETURN(i == 1);
+
+		/*
+		 * move the cursor now to make it easy to continue the loop and
+		 * detect the last extent in the lookup.
+		 */
+		error = xfs_btree_increment(cur, 0, &i);
+		if (error)
+			break;
+
+		/* range check - must be wholly withing requested range */
+		if (fbno < sagbno ||
+		    (eagbno != NULLAGBLOCK && fbno + flen > eagbno)) {
+		xfs_warn(cur->bc_mp, "10: %d/%d, %d/%d", sagbno, eagbno, fbno, flen);
+			continue;
+		}
+
+		/*
+		 * use daddr format for all range/len calculations as that is
+		 * the format the range/len variables are supplied in by
+		 * userspace.
+		 */
+		dbno = XFS_AGB_TO_DADDR(cur->bc_mp, cur->bc_private.a.agno, fbno);
+		dlen = XFS_FSB_TO_BB(cur->bc_mp, flen);
+
+		if (i == 0)
+			flags |= FIEMAP_EXTENT_LAST;
+		error = -fiemap_fill_next_extent(fieinfo, BBTOB(dbno),
+						BBTOB(dbno), BBTOB(dlen), flags);
+		if (error)
+			break;
+	}
+	return error;
+
+}
+
+/*
+ * Map the freespace from the requested range in the requested order.
+ *
+ * To make things simple, this function will only return the freespace from a
+ * single AG regardless of the size of the map passed in. That AG will be the AG
+ * that the first freespace is found in. In other words, FIEMAP_EXTENT_LAST does
+ * not mean the last freespace extent has been mapped, just that the last extent
+ * in a given freespace index has been mapped. The caller is responsible for
+ * moving the range to the next freespace region if it needs to query for more
+ * information.
+ *
+ * IOWs, the caller is responsible for knowing about the XFS filesystem
+ * structure and how it indexes freespace to use this call effectively.
+ */
+#define XFS_FREESP_FLAGS (FIEMAP_FLAG_FREESPACE | FIEMAP_FLAG_FREESPACE_SIZE)
+int
+xfs_alloc_freespace_map(
+	struct xfs_mount	*mp,
+	struct fiemap_extent_info *fieinfo,
+	u64			start,
+	u64			length)
+{
+	struct xfs_btree_cur	*cur;
+	struct xfs_buf		*agbp;
+	struct xfs_perag	*pag;
+	xfs_agnumber_t		agno;
+	xfs_agnumber_t		sagno;
+	xfs_agblock_t		sagbno;
+	xfs_agnumber_t		eagno;
+	xfs_agblock_t		eagbno;
+	bool			bycnt;
+	int			error = 0;
+
+	/* can only have one type of mapping */
+	if ((fieinfo->fi_flags & XFS_FREESP_FLAGS) == XFS_FREESP_FLAGS) {
+		xfs_warn(mp, "1: 0x%x\n", fieinfo->fi_flags);
+		return EINVAL;
+	}
+	bycnt = (fieinfo->fi_flags & FIEMAP_FLAG_FREESPACE_SIZE);
+
+	if (XFS_B_TO_FSB(mp, start) >= mp->m_sb.sb_dblocks) {
+		xfs_warn(mp, "2: %lld, %lld/%lld\n", start,
+				XFS_B_TO_FSB(mp, start), mp->m_sb.sb_dblocks);
+		return EINVAL;
+	}
+	if (length < mp->m_sb.sb_blocksize) {
+		xfs_warn(mp, "3: %lld, %d\n", length, mp->m_sb.sb_blocksize);
+		return EINVAL;
+	}
+	if (start + length < start) {
+		xfs_warn(mp, "4: %lld/%lld, %lld", start, length, start + length);
+		return EINVAL;
+	}
+
+	sagno = xfs_daddr_to_agno(mp, BTOBB(start));
+	sagbno = xfs_daddr_to_agbno(mp, BTOBB(start));
+
+	eagno = xfs_daddr_to_agno(mp, BTOBB(start + length));
+	eagbno = xfs_daddr_to_agbno(mp, BTOBB(start + length));
+
+	if (sagno == eagno && sagbno == eagbno) {
+		xfs_warn(mp, "5: %d/%d, %d/%d", sagno, eagno, sagbno, eagbno);
+		return EINVAL;
+	}
+
+	/*
+	 * Force out the log.  This means any transactions that might have freed
+	 * space before we took the AGF buffer lock are now on disk, and the
+	 * volatile disk cache is flushed.
+	 */
+	xfs_log_force(mp, XFS_LOG_SYNC);
+
+	/*
+	 * Do initial lookup in by-bno tree. Keep skipping AGs until with
+	 * either find a free space extent or reach the end of the search.
+	 */
+	for (agno = sagno; agno < eagno; agno++) {
+		int	i;
+		error = 0;
+
+		error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+		if (error || !agbp) {
+			xfs_warn(mp, "7: %p, %d", agbp, error);
+			goto next;
+		}
+
+		pag = xfs_perag_get(mp, agno);
+		if (pag->pagf_freeblks <= pag->pagf_flcount) {
+			/* no free space worth reporting */
+			xfs_warn(mp, "6: %d %d", pag->pagf_freeblks,
+						pag->pagf_flcount);
+			goto put_agbp;
+		}
+
+		cur = xfs_allocbt_init_cursor(mp, NULL, agbp, agno,
+					      XFS_BTNUM_BNO);
+		error = xfs_alloc_lookup_ge(cur, 0, sagbno, &i);
+		if (error) {
+			xfs_warn(mp, "8: %d/%d, %d/%d", sagno, eagno, sagbno, eagbno);
+			goto del_cursor;
+		}
+		XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor);
+
+		if (!bycnt) {
+			/*
+			 * if we are doing a bno ordered lookup, we can just
+			 * loop across the free space extents formatting them
+			 * until we get to the end of the AG, eagbno or fill the
+			 * fieinfo map.
+			 */
+			error = xfs_alloc_ag_freespace_map(cur, fieinfo, sagbno,
+					agno == eagno ? eagbno : NULLAGBLOCK);
+		} else {
+			/*
+			 * We are doing a size ordered lookup. We know there is
+			 * a free space extent somewhere past out start bno, so
+			 * just kill the current cursor and start a size
+			 * ordered scan to find all the freespace in the given
+			 * range.
+			 */
+			xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+			cur = xfs_allocbt_init_cursor(mp, NULL, agbp, agno,
+						      XFS_BTNUM_CNT);
+			error = xfs_alloc_lookup_ge(cur, 0, 1, &i);
+			if (error)
+				goto del_cursor;
+			XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor);
+
+			error = xfs_alloc_ag_freespace_map(cur, fieinfo, sagbno,
+					agno == eagno ? eagbno : NULLAGBLOCK);
+		}
+
+del_cursor:
+		xfs_btree_del_cursor(cur, error < 0 ? XFS_BTREE_ERROR
+						    : XFS_BTREE_NOERROR);
+put_agbp:
+		xfs_perag_put(pag);
+		xfs_buf_relse(agbp);
+next:
+		if (error)
+			break;
+		sagbno = 0;
+	}
+
+	/*
+	 * negative errno indicates that we hit a FIEMAP_EXTENT_LAST flag. Clear
+	 * the error in that case.
+	 */
+	if (error < 0)
+		error = 0;
+
+	return error;;
+}
diff --git a/fs/xfs/xfs_alloc.h b/fs/xfs/xfs_alloc.h
index feacb06..371b02c 100644
--- a/fs/xfs/xfs_alloc.h
+++ b/fs/xfs/xfs_alloc.h
@@ -231,4 +231,11 @@ xfs_alloc_get_rec(
 	xfs_extlen_t		*len,	/* output: length of extent */
 	int			*stat);	/* output: success/failure */
 
+int
+xfs_alloc_freespace_map(
+	struct xfs_mount	*mp,
+	struct fiemap_extent_info *fieinfo,
+	u64			start,
+	u64			length);
+
 #endif	/* __XFS_ALLOC_H__ */
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 4e00cf0..4555525 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -938,7 +938,9 @@ xfs_vn_update_time(
 	return -xfs_trans_commit(tp, 0);
 }
 
-#define XFS_FIEMAP_FLAGS	(FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)
+#define XFS_FIEMAP_FLAGS	(FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR | \
+				 FIEMAP_FLAG_FREESPACE |		\
+				 FIEMAP_FLAG_FREESPACE_SIZE)
 
 /*
  * Call fiemap helper to fill in user data.
@@ -997,6 +999,13 @@ xfs_vn_fiemap(
 	if (error)
 		return error;
 
+	if ((fieinfo->fi_flags &
+			(FIEMAP_FLAG_FREESPACE | FIEMAP_FLAG_FREESPACE_SIZE))) {
+		error = xfs_alloc_freespace_map(ip->i_mount, fieinfo,
+						start, length);
+		goto out;
+	}
+
 	/* Set up bmap header for xfs internal routine */
 	bm.bmv_offset = BTOBB(start);
 	/* Special case for whole file */
@@ -1017,6 +1026,7 @@ xfs_vn_fiemap(
 		bm.bmv_iflags |= BMV_IF_DELALLOC;
 
 	error = xfs_getbmap(ip, &bm, xfs_fiemap_format, fieinfo);
+out:
 	if (error)
 		return -error;
 
-- 
1.7.10

_______________________________________________
xfs mailing list
xfs@xxxxxxxxxxx
http://oss.sgi.com/mailman/listinfo/xfs


[Index of Archives]     [Linux XFS Devel]     [Linux Filesystem Development]     [Filesystem Testing]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux