Skip to contentSkip to conversation

xfs: add support for tracking inodes with post-EOF speculative preallocation

RFC PATCH Superseded Brian Foster 2 participants 19 messages Aug 27, 2012 – Sep 5, 2012 XFS filesystem

v1 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

cover xfs: add support for tracking inodes with post-EOF speculative preallocation

Hi all,

The following patchset adds support for tracking and clearing inodes with post-EOF space due to speculative preallocation. The purpose of this work is to add some flexibility and management control over inodes in this state. The specific impetus for this work is to support improved quota management in the glusterfs cluster filesystem. For example, this provides the ability to potentially free up a significant amount of unwritten space when at or near EDQUOT (FWIW, I'm also working on a patch to support prealloc throttling near EDQUOT, but I'm considering that separate). Despite the motivation, this work is intended to be generic and hopefully useful for other XFS users.

We track inodes with post-EOF space via a new tag in the per-ag inode radix tree. The tag management, introduced in patch 1, is generally lazy in an attempt to keep things simple. Patch 2 implements scanning functionality to locate and potentially trim inodes with post-EOF space. Patch 3 adds a new ioctl() to extend support to userspace. Patch 4 adds background scanning based on the existing syncd workqueue.

This patchset is marked RFC because aside from general review, I have some questions on the best approach for several bits (marked with 'TODO' comments), most notably whether we should include new tunables for the background scanning. Some further testing is warranted, but I suspect that using the syncd default of 30s might be a bit too aggressive for large files.

These patches have been lightly unit tested and run through xfstests. Thoughts appreciated.

Brian

P.S., Credit to Dave Chinner for early review and guidance on design.

Brian Foster (4):
xfs: add EOFBLOCKS inode tagging/untagging
xfs: create function to scan and clear EOFBLOCKS inodes
xfs: add FREE_EOFBLOCKS ioctl
xfs: add background scanning to clear EOFBLOCKS inodes

 fs/xfs/xfs_ag.h       |    1 +
 fs/xfs/xfs_fs.h       |   10 ++
 fs/xfs/xfs_inode.h    |    8 ++-
 fs/xfs/xfs_ioctl.c    |   25 +++++
 fs/xfs/xfs_iomap.c    |    7 ++
 fs/xfs/xfs_iops.c     |    3 +
 fs/xfs/xfs_mount.h    |    2 +
 fs/xfs/xfs_quota.h    |    1 +
 fs/xfs/xfs_quotaops.c |    2 +-
 fs/xfs/xfs_sync.c     |  277 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_sync.h     |    9 ++
 fs/xfs/xfs_trace.h    |    5 +
 fs/xfs/xfs_vnodeops.c |   16 ++-
 fs/xfs/xfs_vnodeops.h |    2 +
 14 files changed, 362 insertions(+), 6 deletions(-)
Signature · 6 lines
1.7.7.6

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#1 lore raw

RFC PATCH 1/4 xfs: add EOFBLOCKS inode tagging/untagging

Add the XFS_ICI_EOFBLOCKS_TAG inode tag to identify inodes with speculatively preallocated blocks beyond EOF. An inode is tagged when speculative preallocation occurs and untagged either via truncate down or when post-EOF blocks are freed via release or reclaim.

The tag management is intentionally not aggressive to prefer simplicity over the complexity of handling all the corner cases under which post-EOF blocks could be freed (i.e., forward truncation, fallocate, write error conditions, etc.). This means that a tagged inode may or may not have post-EOF blocks after a period of time. The tag is eventually cleared when the inode is released or reclaimed.

---
 fs/xfs/xfs_ag.h       |    1 +
 fs/xfs/xfs_inode.h    |    8 ++++-
 fs/xfs/xfs_iomap.c    |    7 ++++
 fs/xfs/xfs_iops.c     |    3 ++
 fs/xfs/xfs_sync.c     |   76 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_sync.h     |    3 ++
 fs/xfs/xfs_trace.h    |    5 +++
 fs/xfs/xfs_vnodeops.c |    3 ++
 8 files changed, 105 insertions(+), 1 deletions(-)
fs/xfs/xfs_ag.h +1 -0
index 44d65c1..22bd4db 100644
@@ -233,6 +233,7 @@ typedef struct xfs_perag {
 #define XFS_ICI_NO_TAG		(-1)	/* special flag for an untagged lookup
 					   in xfs_inode_ag_iterator */
 #define XFS_ICI_RECLAIM_TAG	0	/* inode is to be reclaimed */
+#define XFS_ICI_EOFBLOCKS_TAG	1	/* inode has blocks beyond EOF */
 
 #define	XFS_AG_MAXLEVELS(mp)		((mp)->m_ag_maxlevels)
 #define	XFS_MIN_FREELIST_RAW(bl,cl,mp)	\
fs/xfs/xfs_inode.h +7 -1
index 94b32f9..2cd2883 100644
@@ -305,10 +305,16 @@ xfs_iflags_set(xfs_inode_t *ip, unsigned short flags)
 }
 
 static inline void
+__xfs_iflags_clear(xfs_inode_t *ip, unsigned short flags)
+{
+	ip->i_flags &= ~flags;
+}
+
+static inline void
 xfs_iflags_clear(xfs_inode_t *ip, unsigned short flags)
 {
 	spin_lock(&ip->i_flags_lock);
-	ip->i_flags &= ~flags;
+	__xfs_iflags_clear(ip, flags);
 	spin_unlock(&ip->i_flags_lock);
 }
 
fs/xfs/xfs_iomap.c +7 -0
index 973dff6..2968ee8 100644
@@ -459,6 +459,13 @@ retry:
 	if (!(imap[0].br_startblock || XFS_IS_REALTIME_INODE(ip)))
 		return xfs_alert_fsblock_zero(ip, &imap[0]);
 
+	/*
+	 * Tag the inode as speculatively preallocated so we can reclaim this
+	 * space on demand, if necessary.
+	 */
+	if (prealloc)
+		xfs_inode_set_eofblocks_tag(ip);
+
 	*ret_imap = imap[0];
 	return 0;
 }
fs/xfs/xfs_iops.c +3 -0
index 4e00cf0..dcd1d5f 100644
@@ -854,6 +854,9 @@ xfs_setattr_size(
 		 * and do not wait the usual (long) time for writeout.
 		 */
 		xfs_iflags_set(ip, XFS_ITRUNCATED);
+
+		/* A truncate down always removes post-EOF blocks. */
+		xfs_inode_clear_eofblocks_tag(ip);
 	}
 
 	if (mask & ATTR_CTIME) {
fs/xfs/xfs_sync.c +76 -0

79 lines not on this page · raw message

fs/xfs/xfs_sync.h +3 -0
index 941202e..4486491 100644
@@ -43,6 +43,9 @@ void __xfs_inode_set_reclaim_tag(struct xfs_perag *pag, struct xfs_inode *ip);
 void __xfs_inode_clear_reclaim_tag(struct xfs_mount *mp, struct xfs_perag *pag,
 				struct xfs_inode *ip);
 
+void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
+void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
+
 int xfs_sync_inode_grab(struct xfs_inode *ip);
 int xfs_inode_ag_iterator(struct xfs_mount *mp,
 	int (*execute)(struct xfs_inode *ip, struct xfs_perag *pag, int flags),
fs/xfs/xfs_trace.h +5 -0
index e5795dd..47e37da 100644
@@ -129,6 +129,8 @@ DEFINE_PERAG_REF_EVENT(xfs_perag_get_tag);
 DEFINE_PERAG_REF_EVENT(xfs_perag_put);
 DEFINE_PERAG_REF_EVENT(xfs_perag_set_reclaim);
 DEFINE_PERAG_REF_EVENT(xfs_perag_clear_reclaim);
+DEFINE_PERAG_REF_EVENT(xfs_perag_set_eofblocks);
+DEFINE_PERAG_REF_EVENT(xfs_perag_clear_eofblocks);
 
 TRACE_EVENT(xfs_attr_list_node_descend,
 	TP_PROTO(struct xfs_attr_list_context *ctx,
@@ -584,6 +586,9 @@ DEFINE_INODE_EVENT(xfs_update_time);
 DEFINE_INODE_EVENT(xfs_dquot_dqalloc);
 DEFINE_INODE_EVENT(xfs_dquot_dqdetach);
 
+DEFINE_INODE_EVENT(xfs_set_eofblocks_tag);
+DEFINE_INODE_EVENT(xfs_clear_eofblocks_tag);
+
 DECLARE_EVENT_CLASS(xfs_iref_class,
 	TP_PROTO(struct xfs_inode *ip, unsigned long caller_ip),
 	TP_ARGS(ip, caller_ip),
fs/xfs/xfs_vnodeops.c +3 -0
index 2a5c6373..658ee2e 100644
@@ -467,6 +467,8 @@ xfs_release(
 		if (error)
 			return error;
 
+		xfs_inode_clear_eofblocks_tag(ip);
+
 		/* delalloc blocks after truncation means it really is dirty */
 		if (ip->i_delayed_blks)
 			xfs_iflags_set(ip, XFS_IDIRTY_RELEASE);
@@ -523,6 +525,7 @@ xfs_inactive(
 			error = xfs_free_eofblocks(mp, ip, false);
 			if (error)
 				return VN_INACTIVE_CACHE;
+			xfs_inode_clear_eofblocks_tag(ip);
 		}
 		goto out;
 	}
Signature · 6 lines
1.7.7.6

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#2 lore raw

Re: [RFC PATCH 1/4] xfs: add EOFBLOCKS inode tagging/untagging

Quoted 15 lines · Brian Foster

Add the XFS_ICI_EOFBLOCKS_TAG inode tag to identify inodes with speculatively preallocated blocks beyond EOF. An inode is tagged when speculative preallocation occurs and untagged either via truncate down or when post-EOF blocks are freed via release or reclaim.

The tag management is intentionally not aggressive to prefer simplicity over the complexity of handling all the corner cases under which post-EOF blocks could be freed (i.e., forward truncation, fallocate, write error conditions, etc.). This means that a tagged inode may or may not have post-EOF blocks after a period of time. The tag is eventually cleared when the inode is released or reclaimed.

.....

Quoted 11 lines
fs/xfs/xfs_inode.h +3 -0
index 94b32f9..2cd2883 100644
@@ -305,10 +305,16 @@ xfs_iflags_set(xfs_inode_t *ip, unsigned short flags)
 }
 
 static inline void
+__xfs_iflags_clear(xfs_inode_t *ip, unsigned short flags)
+{
+	ip->i_flags &= ~flags;

fs/xfs/xfs_inode.h xfs_iflags_set(xfs_inode_t *ip, unsigned short flags) · line 319

Quoted 10 lines
fs/xfs/xfs_inode.h +4 -1
+}
+
+static inline void
 xfs_iflags_clear(xfs_inode_t *ip, unsigned short flags)
 {
 	spin_lock(&ip->i_flags_lock);
-	ip->i_flags &= ~flags;
+	__xfs_iflags_clear(ip, flags);
 	spin_unlock(&ip->i_flags_lock);
 }

Left overs from a previous version?

Quoted 70 lines
fs/xfs/xfs_iomap.c +7 -0
index 973dff6..2968ee8 100644
@@ -459,6 +459,13 @@ retry:
 	if (!(imap[0].br_startblock || XFS_IS_REALTIME_INODE(ip)))
 		return xfs_alert_fsblock_zero(ip, &imap[0]);
 
+	/*
+	 * Tag the inode as speculatively preallocated so we can reclaim this
+	 * space on demand, if necessary.
+	 */
+	if (prealloc)
+		xfs_inode_set_eofblocks_tag(ip);
+
 	*ret_imap = imap[0];
 	return 0;
 }
fs/xfs/xfs_iops.c +3 -0
index 4e00cf0..dcd1d5f 100644
@@ -854,6 +854,9 @@ xfs_setattr_size(
 		 * and do not wait the usual (long) time for writeout.
 		 */
 		xfs_iflags_set(ip, XFS_ITRUNCATED);
+
+		/* A truncate down always removes post-EOF blocks. */
+		xfs_inode_clear_eofblocks_tag(ip);
 	}
 
 	if (mask & ATTR_CTIME) {
fs/xfs/xfs_sync.c +29 -0
index 9654817..5e14741 100644
@@ -971,3 +971,79 @@ xfs_reclaim_inodes_count(
 	return reclaimable;
 }
 
+STATIC void
+__xfs_inode_set_eofblocks_tag(
+	struct xfs_perag	*pag,
+	struct xfs_inode	*ip)
+{
+	int tagged = radix_tree_tagged(&pag->pag_ici_root,
+				       XFS_ICI_EOFBLOCKS_TAG);
+
+	radix_tree_tag_set(&pag->pag_ici_root,
+			   XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino),
+			   XFS_ICI_EOFBLOCKS_TAG);
+
+	if (!tagged) {
+		/* propagate the eofblocks tag up into the perag radix tree */
+		spin_lock(&ip->i_mount->m_perag_lock);
+		radix_tree_tag_set(&ip->i_mount->m_perag_tree,
+				   XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino),
+				   XFS_ICI_EOFBLOCKS_TAG);
+		spin_unlock(&ip->i_mount->m_perag_lock);
+
+		trace_xfs_perag_set_eofblocks(ip->i_mount, pag->pag_agno,
+					      -1, _RET_IP_);
+	}
+}
+
+void
+xfs_inode_set_eofblocks_tag(
+	xfs_inode_t	*ip)
+{

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1012

Quoted 10 lines
fs/xfs/xfs_sync.c +10 -0
+	struct xfs_mount *mp = ip->i_mount;
+	struct xfs_perag *pag;
+
+	pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
+	spin_lock(&pag->pag_ici_lock);
+	trace_xfs_set_eofblocks_tag(ip);
+	__xfs_inode_set_eofblocks_tag(pag, ip);
+	spin_unlock(&pag->pag_ici_lock);
+	xfs_perag_put(pag);
+}

I know the code you copied had this two-function structure, but that was because it has callers of the __ versions of the functions. I'd just make these a single function.

Quoted 26 lines
fs/xfs/xfs_sync.c +26 -0
+
+STATIC void
+__xfs_inode_clear_eofblocks(
+	xfs_perag_t	*pag,
+	xfs_inode_t	*ip)
+{
+	radix_tree_tag_clear(&pag->pag_ici_root,
+			     XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino),
+			     XFS_ICI_EOFBLOCKS_TAG);
+
+	if (!radix_tree_tagged(&pag->pag_ici_root, XFS_ICI_EOFBLOCKS_TAG)) {
+		/* clear the eofblocks tag from the perag radix tree */
+		spin_lock(&ip->i_mount->m_perag_lock);
+		radix_tree_tag_clear(&ip->i_mount->m_perag_tree,
+				     XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino),
+				     XFS_ICI_EOFBLOCKS_TAG);
+		spin_unlock(&ip->i_mount->m_perag_lock);
+		trace_xfs_perag_clear_eofblocks(ip->i_mount, pag->pag_agno,
+					       -1, _RET_IP_);
+	}
+}
+
+void
+xfs_inode_clear_eofblocks_tag(
+	xfs_inode_t	*ip)
+{

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1048

Quoted 10 lines
fs/xfs/xfs_sync.c +10 -0
+	struct xfs_mount *mp = ip->i_mount;
+	struct xfs_perag *pag;
+
+	pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
+	spin_lock(&pag->pag_ici_lock);
+	trace_xfs_clear_eofblocks_tag(ip);
+	__xfs_inode_clear_eofblocks(pag, ip);
+	spin_unlock(&pag->pag_ici_lock);
+	xfs_perag_put(pag);
+}

Same here.

Quoted 14 lines
fs/xfs/xfs_vnodeops.c +2 -0
index 2a5c6373..658ee2e 100644
@@ -467,6 +467,8 @@ xfs_release(
 		if (error)
 			return error;
 
+		xfs_inode_clear_eofblocks_tag(ip);
+
 		/* delalloc blocks after truncation means it really is dirty */
 		if (ip->i_delayed_blks)
 			xfs_iflags_set(ip, XFS_IDIRTY_RELEASE);

fs/xfs/xfs_vnodeops.c xfs_inactive( · line 530

Quoted 6 lines
fs/xfs/xfs_vnodeops.c +1 -0
@@ -523,6 +525,7 @@ xfs_inactive(
 			error = xfs_free_eofblocks(mp, ip, false);
 			if (error)
 				return VN_INACTIVE_CACHE;
+			xfs_inode_clear_eofblocks_tag(ip);
 		}
 		goto out;

I think it's better to call xfs_inode_clear_eofblocks_tag() inside xfs_free_eofblocks() - that's where the blocks are being freed, after all.

Cheers,

Dave.

Signature · 7 lines
Dave Chinner
[email protected]

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#3 lore raw

RFC PATCH 2/4 xfs: create function to scan and clear EOFBLOCKS inodes

In reply to Brian Foster

xfs_inodes_free_eofblocks() implements scanning functionality for EOFBLOCKS inodes. It scans the radix tree and frees post-EOF blocks for inodes that meet particular criteria. The scan can be filtered by a particular quota type/id and minimum file size. The scan can also be invoked in trylock mode or wait (force) mode.

The xfs_free_eofblocks() helper is invoked to clear post-EOF space. It is slightly modified to support an output parameter that indicates whether space was freed and helps decide whether the EOFBLOCKS tag should be cleared in trylock scans.

---
 fs/xfs/xfs_sync.c     |  168 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_sync.h     |    3 +
 fs/xfs/xfs_vnodeops.c |   17 +++--
 fs/xfs/xfs_vnodeops.h |    2 +
 4 files changed, 184 insertions(+), 6 deletions(-)
fs/xfs/xfs_sync.c +168 -0
index 5e14741..27c3c46 100644
@@ -971,6 +971,174 @@ xfs_reclaim_inodes_count(
 	return reclaimable;
 }
 
+/*
+ * Handle an EOFBLOCKS tagged inode. If this is a forced scan, we wait on the
+ * iolock ourselves rather than rely on the trylock in xfs_free_eofblocks(). 
+ *
+ * We rely on the output parameter from xfs_free_eofblocks() to determine
+ * whether we should clear the tag because in the trylock case, it could have
+ * skipped the inode due to lock contention.
+ */
+STATIC int
+xfs_inode_free_eofblocks(
+	struct xfs_inode	*ip,
+	int			flags)
+{
+	int ret = 0;
+	bool freed = false;
+	bool wait_iolock = (flags & EOFBLOCKS_WAIT) ? true : false;
+
+	if (wait_iolock)
+		xfs_ilock(ip, XFS_IOLOCK_EXCL);
+
+	if ((S_ISREG(ip->i_d.di_mode) &&
+	     (VFS_I(ip)->i_size > 0 ||
+	     (VN_CACHED(VFS_I(ip)) > 0 || ip->i_delayed_blks > 0)) &&
+	     (ip->i_df.if_flags & XFS_IFEXTENTS)) &&
+	    (!(ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)))) {
+		/* !wait_iolock == need_iolock in xfs_free_eofblocks() */
+		ret = xfs_free_eofblocks(ip->i_mount, ip, !wait_iolock, &freed);
+		if (freed)
+			xfs_inode_clear_eofblocks_tag(ip);
+	} else {
+		/* inode could be preallocated or append-only */
+		xfs_inode_clear_eofblocks_tag(ip);
+	}
+
+	if (wait_iolock)
+		xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+
+	return ret;
+}
+
+/*
+ * Determine whether an inode matches a particular qouta id.
+ */
+STATIC int
+xfs_inode_match_quota_id(
+	struct xfs_inode	*ip,
+	int			qtype,
+	uint32_t		id)
+{
+	switch (qtype) {
+	case XFS_DQ_USER:
+		return ip->i_d.di_uid == id;
+	case XFS_DQ_GROUP:
+		return ip->i_d.di_gid == id;
+	default:
+		return xfs_get_projid(ip) == id;
+	}
+
+	return 0;
+}
+
+/*
+ * This is mostly copied from xfs_reclaim_inodes_ag().
+ *
+ * TODO:
+ * - Could we enhance ag_iterator to support a tag and use it instead of this?
+ */
+int
+xfs_inodes_free_eofblocks(
+	struct xfs_mount	*mp,
+	int			qtype,
+	uint32_t		id,
+	uint64_t		min_file_size,
+	int			flags)
+{
+	struct xfs_perag	*pag;
+	int			error = 0;
+	int			last_error = 0;
+	xfs_agnumber_t		ag;
+
+	ag = 0;
+	while ((pag = xfs_perag_get_tag(mp, ag, XFS_ICI_EOFBLOCKS_TAG))) {
+		unsigned long	first_index = 0;
+		int		nr_found = 0;
+		int		done = 0;
+
+		ag = pag->pag_agno + 1;
+
+		do {
+			struct xfs_inode *batch[XFS_LOOKUP_BATCH];
+			int	i;
+
+			rcu_read_lock();
+			nr_found = radix_tree_gang_lookup_tag(
+					&pag->pag_ici_root,
+					(void **)batch, first_index,
+					XFS_LOOKUP_BATCH,
+					XFS_ICI_EOFBLOCKS_TAG);
+			if (!nr_found) {
+				rcu_read_unlock();
+				break;
+			}
+
+			/*
+			 * Grab the inodes before we drop the lock. if we found
+			 * nothing, nr == 0 and the loop will be skipped.
+			 */
+			for (i = 0; i < nr_found; i++) {
+				struct xfs_inode *ip = batch[i];
+
+				if (done || xfs_inode_ag_walk_grab(ip))
+					batch[i] = NULL;
+
+				/*
+				 * Update the index for the next lookup. Catch
+				 * overflows into the next AG range which can occur if
+				 * we have inodes in the last block of the AG and we
+				 * are currently pointing to the last inode.
+				 *
+				 * Because we may see inodes that are from the wrong AG
+				 * due to RCU freeing and reallocation, only update the
+				 * index if it lies in this AG. It was a race that lead
+				 * us to see this inode, so another lookup from the
+				 * same index will not find it again.
+				 */
+				if (XFS_INO_TO_AGNO(mp, ip->i_ino) !=
+								pag->pag_agno)
+					continue;
+				first_index = XFS_INO_TO_AGINO(mp, ip->i_ino + 1);
+				if (first_index < XFS_INO_TO_AGINO(mp, ip->i_ino))
+					done = 1;
+			}
+
+			/* unlock now we've grabbed the inodes. */
+			rcu_read_unlock();
+
+			for (i = 0; i < nr_found; i++) {
+				if (!batch[i])
+					continue;
+
+				/* default projid represents a full scan */
+				if ((!(qtype == XFS_DQ_PROJ &&
+				       id == XFS_PROJID_DEFAULT) &&
+				     !xfs_inode_match_quota_id(batch[i], qtype,
+							       id)) ||
+				    (min_file_size && XFS_ISIZE(batch[i]) < 
+								min_file_size)
+				   ) {
+					IRELE(batch[i]);
+					continue;
+				}
+
+				error = xfs_inode_free_eofblocks(batch[i], flags);
+				IRELE(batch[i]);
+				if (error)
+					last_error = error;
+			}
+
+			cond_resched();
+
+		} while (nr_found && !done);
+
+		xfs_perag_put(pag);
+	}
+
+	return XFS_ERROR(last_error);
+}
+
 STATIC void
 __xfs_inode_set_eofblocks_tag(
 	struct xfs_perag	*pag,
fs/xfs/xfs_sync.h +3 -0
index 4486491..78aca41 100644
@@ -43,8 +43,11 @@ void __xfs_inode_set_reclaim_tag(struct xfs_perag *pag, struct xfs_inode *ip);
 void __xfs_inode_clear_reclaim_tag(struct xfs_mount *mp, struct xfs_perag *pag,
 				struct xfs_inode *ip);
 
+#define EOFBLOCKS_WAIT		0x0001
+
 void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
 void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
+int xfs_inodes_free_eofblocks(struct xfs_mount *, int, uint32_t, uint64_t, int);
 
 int xfs_sync_inode_grab(struct xfs_inode *ip);
 int xfs_inode_ag_iterator(struct xfs_mount *mp,
fs/xfs/xfs_vnodeops.c +11 -6
index 658ee2e..53460f3 100644
@@ -150,11 +150,12 @@ xfs_readlink(
  * when the link count isn't zero and by xfs_dm_punch_hole() when
  * punching a hole to EOF.
  */
-STATIC int
+int
 xfs_free_eofblocks(
 	xfs_mount_t	*mp,
 	xfs_inode_t	*ip,
-	bool		need_iolock)
+	bool		need_iolock,
+	bool		*blocks_freed)
 {
 	xfs_trans_t	*tp;
 	int		error;
@@ -237,6 +238,9 @@ xfs_free_eofblocks(
 		} else {
 			error = xfs_trans_commit(tp,
 						XFS_TRANS_RELEASE_LOG_RES);
+			if (blocks_freed)
+				*blocks_freed = true;
+
 		}
 
 		xfs_iunlock(ip, XFS_ILOCK_EXCL);
@@ -391,6 +395,7 @@ xfs_release(
 {
 	xfs_mount_t	*mp = ip->i_mount;
 	int		error;
+	bool		freed = false;
 
 	if (!S_ISREG(ip->i_d.di_mode) || (ip->i_d.di_mode == 0))
 		return 0;
@@ -463,11 +468,11 @@ xfs_release(
 		if (xfs_iflags_test(ip, XFS_IDIRTY_RELEASE))
 			return 0;
 
-		error = xfs_free_eofblocks(mp, ip, true);
+		error = xfs_free_eofblocks(mp, ip, true, &freed);
 		if (error)
 			return error;
-
-		xfs_inode_clear_eofblocks_tag(ip);
+		if (freed)
+			xfs_inode_clear_eofblocks_tag(ip);
 
 		/* delalloc blocks after truncation means it really is dirty */
 		if (ip->i_delayed_blks)
@@ -522,7 +527,7 @@ xfs_inactive(
 		    (!(ip->i_d.di_flags &
 				(XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) ||
 		     ip->i_delayed_blks != 0))) {
-			error = xfs_free_eofblocks(mp, ip, false);
+			error = xfs_free_eofblocks(mp, ip, false, NULL);
 			if (error)
 				return VN_INACTIVE_CACHE;
 			xfs_inode_clear_eofblocks_tag(ip);
fs/xfs/xfs_vnodeops.h +2 -0
index 447e146..918d24d 100644
@@ -57,5 +57,7 @@ int xfs_flush_pages(struct xfs_inode *ip, xfs_off_t first,
 int xfs_wait_on_pages(struct xfs_inode *ip, xfs_off_t first, xfs_off_t last);
 
 int xfs_zero_eof(struct xfs_inode *, xfs_off_t, xfs_fsize_t);
+int xfs_free_eofblocks(struct xfs_mount *, struct xfs_inode *, bool, bool *);
+
 
 #endif /* _XFS_VNODEOPS_H */
Signature · 6 lines
1.7.7.6

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#4 lore raw

Re: [RFC PATCH 2/4] xfs: create function to scan and clear EOFBLOCKS inodes

Quoted 36 lines · Brian Foster

xfs_inodes_free_eofblocks() implements scanning functionality for EOFBLOCKS inodes. It scans the radix tree and frees post-EOF blocks for inodes that meet particular criteria. The scan can be filtered by a particular quota type/id and minimum file size. The scan can also be invoked in trylock mode or wait (force) mode.

The xfs_free_eofblocks() helper is invoked to clear post-EOF space. It is slightly modified to support an output parameter that indicates whether space was freed and helps decide whether the EOFBLOCKS tag should be cleared in trylock scans.

---
 fs/xfs/xfs_sync.c     |  168 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_sync.h     |    3 +
 fs/xfs/xfs_vnodeops.c |   17 +++--
 fs/xfs/xfs_vnodeops.h |    2 +
 4 files changed, 184 insertions(+), 6 deletions(-)
fs/xfs/xfs_sync.c +9 -0
index 5e14741..27c3c46 100644
@@ -971,6 +971,174 @@ xfs_reclaim_inodes_count(
 	return reclaimable;
 }
 
+/*
+ * Handle an EOFBLOCKS tagged inode. If this is a forced scan, we wait on the
+ * iolock ourselves rather than rely on the trylock in xfs_free_eofblocks(). 
+ *
+ * We rely on the output parameter from xfs_free_eofblocks() to determine
+ * whether we should clear the tag because in the trylock case, it could have
+ * skipped the inode due to lock contention.
+ */
+STATIC int

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 992

Quoted 10 lines
fs/xfs/xfs_sync.c +10 -0
+xfs_inode_free_eofblocks(
+	struct xfs_inode	*ip,
+	int			flags)
+{
+	int ret = 0;
+	bool freed = false;
+	bool wait_iolock = (flags & EOFBLOCKS_WAIT) ? true : false;
+
+	if (wait_iolock)
+		xfs_ilock(ip, XFS_IOLOCK_EXCL);

Why do we need the IO lock here? xfs_free_eofblocks() does all the necessary locking....

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 998

Quoted 6 lines
fs/xfs/xfs_sync.c +6 -0
+
+	if ((S_ISREG(ip->i_d.di_mode) &&
+	     (VFS_I(ip)->i_size > 0 ||
+	     (VN_CACHED(VFS_I(ip)) > 0 || ip->i_delayed_blks > 0)) &&
+	     (ip->i_df.if_flags & XFS_IFEXTENTS)) &&
+	    (!(ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)))) {

This check is now repeated in 3 places - xfs_inactive, xfs_release and now here. I think it needs a helper.

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1002

Quoted 4 lines
fs/xfs/xfs_sync.c +4 -0
+		/* !wait_iolock == need_iolock in xfs_free_eofblocks() */
+		ret = xfs_free_eofblocks(ip->i_mount, ip, !wait_iolock, &freed);
+		if (freed)
+			xfs_inode_clear_eofblocks_tag(ip);

If you move xfs_inode_clear_eofblocks_tag() inside xfs_free_eofblocks(), there's no need for this extra return value.

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1005

Quoted 3 lines
fs/xfs/xfs_sync.c +3 -0
+	} else {
+		/* inode could be preallocated or append-only */
+		xfs_inode_clear_eofblocks_tag(ip);

This should be a rare event - it's probably worth adding a pair of trace events here for the two cases so we can see if there is ever a significant number of inodes being scanned for prealloc that can't be cleared...

(e.g 'perf top -e xfs:xfs_i*' to count all the inode events)

Quoted 18 lines
fs/xfs/xfs_sync.c +18 -0
+	}
+
+	if (wait_iolock)
+		xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+
+	return ret;
+}
+
+/*
+ * Determine whether an inode matches a particular qouta id.
+ */
+STATIC int
+xfs_inode_match_quota_id(
+	struct xfs_inode	*ip,
+	int			qtype,
+	uint32_t		id)
+{
+	switch (qtype) {

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1033

Quoted 10 lines
fs/xfs/xfs_sync.c +10 -0
+	case XFS_DQ_USER:
+		return ip->i_d.di_uid == id;
+	case XFS_DQ_GROUP:
+		return ip->i_d.di_gid == id;
+	default:
+		return xfs_get_projid(ip) == id;
+	}
+
+	return 0;
+}

There's nothing really quota specific about this scan. I'd leave this functionality to a separate patch once all the core infrastructure is in place.

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1039

Quoted 6 lines
fs/xfs/xfs_sync.c +6 -0
+
+/*
+ * This is mostly copied from xfs_reclaim_inodes_ag().
+ *
+ * TODO:
+ * - Could we enhance ag_iterator to support a tag and use it instead of this?

Yes. This code is too tricky to duplicate for every use case, and this doesn't have special case requirements like the reclaim code.

i.e. the xfs_inode_free_eofblocks() becomes the execute function (and the quota checks move inside that eventually). Passing a tag of "-1" would indicate a non-tag lookup, otherwise use a tag based lookup. Given the extra fields that this version uses, passing a void *args is probably necessary so that a structure can be passed to the execute function along with the flags....

I'd suggest this conversion should be done in a patch prior to introducing this scanner.

FWIW, this is going to conflict with my "get rid of xfs-sync.c patch series, so we'll need to work out who rebases what at some point.

Quoted 9 lines
fs/xfs/xfs_sync.c +9 -0
+ */
+int
+xfs_inodes_free_eofblocks(
+	struct xfs_mount	*mp,
+	int			qtype,
+	uint32_t		id,
+	uint64_t		min_file_size,
+	int			flags)
+{

.....

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1114

Quoted 5 lines
fs/xfs/xfs_sync.c +5 -0
+			for (i = 0; i < nr_found; i++) {
+				if (!batch[i])
+					continue;
+
+				/* default projid represents a full scan */

I don't think thats a good idea. From a normal users perspective, the background trimming will occur irrespective of the quota groups the inode is part of. Background trimming defines the default behaviour, because that's what 99.99% of users will see active, not quota/application specific events driven through ioctls.

IOWs, selecting inodes by quota type/id for pruning is a secondary function of the execute implementation, not a primary concern of the infrastructure.

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1124

Quoted 11 lines
fs/xfs/xfs_sync.c +10 -0
+				if ((!(qtype == XFS_DQ_PROJ &&
+				       id == XFS_PROJID_DEFAULT) &&
+				     !xfs_inode_match_quota_id(batch[i], qtype,
+							       id)) ||
+				    (min_file_size && XFS_ISIZE(batch[i]) < 
+								min_file_size)
 
+				   ) {
+					IRELE(batch[i]);
+					continue;
+				}

Moving this check to the execute function will get rid of the indent mess....

Quoted 25 lines
fs/xfs/xfs_sync.c +17 -0
+
+				error = xfs_inode_free_eofblocks(batch[i], flags);
+				IRELE(batch[i]);
+				if (error)
+					last_error = error;
+			}
+
+			cond_resched();
+
+		} while (nr_found && !done);
+
+		xfs_perag_put(pag);
+	}
+
+	return XFS_ERROR(last_error);
+}
+
 STATIC void
 __xfs_inode_set_eofblocks_tag(
 	struct xfs_perag	*pag,
fs/xfs/xfs_sync.h +0 -0
index 4486491..78aca41 100644

fs/xfs/xfs_sync.h void __xfs_inode_set_reclaim_tag(struct xfs_perag *pag, struct xfs_inode *ip); · line 46

Quoted 4 lines
fs/xfs/xfs_sync.h +1 -0
@@ -43,8 +43,11 @@ void __xfs_inode_set_reclaim_tag(struct xfs_perag *pag, struct xfs_inode *ip);
 void __xfs_inode_clear_reclaim_tag(struct xfs_mount *mp, struct xfs_perag *pag,
 				struct xfs_inode *ip);
 
+#define EOFBLOCKS_WAIT		0x0001

I'd just reuse SYNC_WAIT and SYNC_TRYLOCK which are already defined and used by the sync and reclaim iterators.

Quoted 13 lines
fs/xfs/xfs_sync.h +2 -0
+
 void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
 void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
+int xfs_inodes_free_eofblocks(struct xfs_mount *, int, uint32_t, uint64_t, int);
 
 int xfs_sync_inode_grab(struct xfs_inode *ip);
 int xfs_inode_ag_iterator(struct xfs_mount *mp,
fs/xfs/xfs_vnodeops.c +0 -0
index 658ee2e..53460f3 100644
@@ -150,11 +150,12 @@ xfs_readlink(
  * when the link count isn't zero and by xfs_dm_punch_hole() when

fs/xfs/xfs_vnodeops.c xfs_readlink( · line 158

Quoted 10 lines
fs/xfs/xfs_vnodeops.c +3 -2
  * punching a hole to EOF.
  */
-STATIC int
+int
 xfs_free_eofblocks(
 	xfs_mount_t	*mp,
 	xfs_inode_t	*ip,
-	bool		need_iolock)
+	bool		need_iolock,
+	bool		*blocks_freed)

I don't really see a point to adding this. Either we removed all the EOF blocks or we didn't, and that means we should just clear the tags directly in this function if it is appropriate.

Cheers,

Dave.

Signature · 7 lines
Dave Chinner
[email protected]

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#5 lore raw

Re: [RFC PATCH 2/4] xfs: create function to scan and clear EOFBLOCKS inodes

Quoted 1 line · Dave Chinner

On Mon, Aug 27, 2012 at 03:51:49PM -0400, Brian Foster wrote:

...

Quoted 14 lines
Quoted 14 lines
fs/xfs/xfs_sync.c +14 -0
+/*
+ * Handle an EOFBLOCKS tagged inode. If this is a forced scan, we wait on the
+ * iolock ourselves rather than rely on the trylock in xfs_free_eofblocks(). 
+ *
+ * We rely on the output parameter from xfs_free_eofblocks() to determine
+ * whether we should clear the tag because in the trylock case, it could have
+ * skipped the inode due to lock contention.
+ */
+STATIC int
+xfs_inode_free_eofblocks(
+	struct xfs_inode	*ip,
+	int			flags)
+{
+	int ret = 0;
Quoted 9 lines

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 992

Quoted 5 lines
fs/xfs/xfs_sync.c +5 -0
+	bool freed = false;
+	bool wait_iolock = (flags & EOFBLOCKS_WAIT) ? true : false;
+
+	if (wait_iolock)
+		xfs_ilock(ip, XFS_IOLOCK_EXCL);

Why do we need the IO lock here? xfs_free_eofblocks() does all the necessary locking....

This was for the wait case (e.g., xfs_free_eofblocks() does a trylock on the IO lock and we want to wait for the lock in this case).

Brian

Quoted 187 lines

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 998

Quoted 6 lines
fs/xfs/xfs_sync.c +6 -0
+
+	if ((S_ISREG(ip->i_d.di_mode) &&
+	     (VFS_I(ip)->i_size > 0 ||
+	     (VN_CACHED(VFS_I(ip)) > 0 || ip->i_delayed_blks > 0)) &&
+	     (ip->i_df.if_flags & XFS_IFEXTENTS)) &&
+	    (!(ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)))) {

This check is now repeated in 3 places - xfs_inactive, xfs_release and now here. I think it needs a helper.

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1002

Quoted 4 lines
fs/xfs/xfs_sync.c +4 -0
+		/* !wait_iolock == need_iolock in xfs_free_eofblocks() */
+		ret = xfs_free_eofblocks(ip->i_mount, ip, !wait_iolock, &freed);
+		if (freed)
+			xfs_inode_clear_eofblocks_tag(ip);

If you move xfs_inode_clear_eofblocks_tag() inside xfs_free_eofblocks(), there's no need for this extra return value.

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1005

Quoted 3 lines
fs/xfs/xfs_sync.c +3 -0
+	} else {
+		/* inode could be preallocated or append-only */
+		xfs_inode_clear_eofblocks_tag(ip);

This should be a rare event - it's probably worth adding a pair of trace events here for the two cases so we can see if there is ever a significant number of inodes being scanned for prealloc that can't be cleared...

(e.g 'perf top -e xfs:xfs_i*' to count all the inode events)

Quoted 18 lines
fs/xfs/xfs_sync.c +18 -0
+	}
+
+	if (wait_iolock)
+		xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+
+	return ret;
+}
+
+/*
+ * Determine whether an inode matches a particular qouta id.
+ */
+STATIC int
+xfs_inode_match_quota_id(
+	struct xfs_inode	*ip,
+	int			qtype,
+	uint32_t		id)
+{
+	switch (qtype) {

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1033

Quoted 10 lines
fs/xfs/xfs_sync.c +10 -0
+	case XFS_DQ_USER:
+		return ip->i_d.di_uid == id;
+	case XFS_DQ_GROUP:
+		return ip->i_d.di_gid == id;
+	default:
+		return xfs_get_projid(ip) == id;
+	}
+
+	return 0;
+}

There's nothing really quota specific about this scan. I'd leave this functionality to a separate patch once all the core infrastructure is in place.

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1039

Quoted 6 lines
fs/xfs/xfs_sync.c +6 -0
+
+/*
+ * This is mostly copied from xfs_reclaim_inodes_ag().
+ *
+ * TODO:
+ * - Could we enhance ag_iterator to support a tag and use it instead of this?

Yes. This code is too tricky to duplicate for every use case, and this doesn't have special case requirements like the reclaim code.

i.e. the xfs_inode_free_eofblocks() becomes the execute function (and the quota checks move inside that eventually). Passing a tag of "-1" would indicate a non-tag lookup, otherwise use a tag based lookup. Given the extra fields that this version uses, passing a void *args is probably necessary so that a structure can be passed to the execute function along with the flags....

I'd suggest this conversion should be done in a patch prior to introducing this scanner.

FWIW, this is going to conflict with my "get rid of xfs-sync.c patch series, so we'll need to work out who rebases what at some point.

Quoted 9 lines

+ */

fs/xfs/xfs_sync.c +8 -0
+int
+xfs_inodes_free_eofblocks(
+	struct xfs_mount	*mp,
+	int			qtype,
+	uint32_t		id,
+	uint64_t		min_file_size,
+	int			flags)
+{

.....

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1114

Quoted 5 lines
fs/xfs/xfs_sync.c +5 -0
+			for (i = 0; i < nr_found; i++) {
+				if (!batch[i])
+					continue;
+
+				/* default projid represents a full scan */

I don't think thats a good idea. From a normal users perspective, the background trimming will occur irrespective of the quota groups the inode is part of. Background trimming defines the default behaviour, because that's what 99.99% of users will see active, not quota/application specific events driven through ioctls.

IOWs, selecting inodes by quota type/id for pruning is a secondary function of the execute implementation, not a primary concern of the infrastructure.

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 1124

Quoted 11 lines
fs/xfs/xfs_sync.c +10 -0
+				if ((!(qtype == XFS_DQ_PROJ &&
+				       id == XFS_PROJID_DEFAULT) &&
+				     !xfs_inode_match_quota_id(batch[i], qtype,
+							       id)) ||
+				    (min_file_size && XFS_ISIZE(batch[i]) < 
+								min_file_size)
 
+				   ) {
+					IRELE(batch[i]);
+					continue;
+				}

Moving this check to the execute function will get rid of the indent mess....

Quoted 25 lines
fs/xfs/xfs_sync.c +17 -0
+
+				error = xfs_inode_free_eofblocks(batch[i], flags);
+				IRELE(batch[i]);
+				if (error)
+					last_error = error;
+			}
+
+			cond_resched();
+
+		} while (nr_found && !done);
+
+		xfs_perag_put(pag);
+	}
+
+	return XFS_ERROR(last_error);
+}
+
 STATIC void
 __xfs_inode_set_eofblocks_tag(
 	struct xfs_perag	*pag,
fs/xfs/xfs_sync.h +0 -0
index 4486491..78aca41 100644

fs/xfs/xfs_sync.h void __xfs_inode_set_reclaim_tag(struct xfs_perag *pag, struct xfs_inode *ip); · line 46

Quoted 4 lines
fs/xfs/xfs_sync.h +1 -0
@@ -43,8 +43,11 @@ void __xfs_inode_set_reclaim_tag(struct xfs_perag *pag, struct xfs_inode *ip);
 void __xfs_inode_clear_reclaim_tag(struct xfs_mount *mp, struct xfs_perag *pag,
 				struct xfs_inode *ip);
 
+#define EOFBLOCKS_WAIT		0x0001

I'd just reuse SYNC_WAIT and SYNC_TRYLOCK which are already defined and used by the sync and reclaim iterators.

Quoted 13 lines
fs/xfs/xfs_sync.h +2 -0
+
 void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
 void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
+int xfs_inodes_free_eofblocks(struct xfs_mount *, int, uint32_t, uint64_t, int);
 
 int xfs_sync_inode_grab(struct xfs_inode *ip);
 int xfs_inode_ag_iterator(struct xfs_mount *mp,
fs/xfs/xfs_vnodeops.c +0 -0
index 658ee2e..53460f3 100644
@@ -150,11 +150,12 @@ xfs_readlink(
  * when the link count isn't zero and by xfs_dm_punch_hole() when

fs/xfs/xfs_vnodeops.c xfs_readlink( · line 158

Quoted 10 lines
fs/xfs/xfs_vnodeops.c +3 -2
  * punching a hole to EOF.
  */
-STATIC int
+int
 xfs_free_eofblocks(
 	xfs_mount_t	*mp,
 	xfs_inode_t	*ip,
-	bool		need_iolock)
+	bool		need_iolock,
+	bool		*blocks_freed)

I don't really see a point to adding this. Either we removed all the EOF blocks or we didn't, and that means we should just clear the tags directly in this function if it is appropriate.

Quoted 4 lines

Cheers,

Dave.

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#6 lore raw

Re: [RFC PATCH 2/4] xfs: create function to scan and clear EOFBLOCKS inodes

Quoted 27 lines · Brian Foster
Quoted 1 line · Dave Chinner

On Mon, Aug 27, 2012 at 03:51:49PM -0400, Brian Foster wrote:

...

Quoted 14 lines
Quoted 14 lines
fs/xfs/xfs_sync.c +14 -0
+/*
+ * Handle an EOFBLOCKS tagged inode. If this is a forced scan, we wait on the
+ * iolock ourselves rather than rely on the trylock in xfs_free_eofblocks(). 
+ *
+ * We rely on the output parameter from xfs_free_eofblocks() to determine
+ * whether we should clear the tag because in the trylock case, it could have
+ * skipped the inode due to lock contention.
+ */
+STATIC int
+xfs_inode_free_eofblocks(
+	struct xfs_inode	*ip,
+	int			flags)
+{
+	int ret = 0;
Quoted 9 lines

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 992

Quoted 5 lines
fs/xfs/xfs_sync.c +5 -0
+	bool freed = false;
+	bool wait_iolock = (flags & EOFBLOCKS_WAIT) ? true : false;
+
+	if (wait_iolock)
+		xfs_ilock(ip, XFS_IOLOCK_EXCL);

Why do we need the IO lock here? xfs_free_eofblocks() does all the necessary locking....

Quoted 2 lines

This was for the wait case (e.g., xfs_free_eofblocks() does a trylock on the IO lock and we want to wait for the lock in this case).

Does it really matter if we can't get the iolock and skip the inode?

Note that xfs_inode_ag_walk() will keep a skipped count if you return EAGAIN, and re-run the walk on the AG if any inodes are skipped. This is done to prevent blocking on locks slowing down the scan - instead of blocking we move on to the next inode, and revist the blocked inodes again in a later pass.

IOWs, if you convert the code to use the generic walk and make xfs_free_eofblocks() return EAGAIN when it can't get the io lock, you get the retry for free...

Cheers,

Dave.

Signature · 7 lines
Dave Chinner
[email protected]

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#7 lore raw

Re: [RFC PATCH 2/4] xfs: create function to scan and clear EOFBLOCKS inodes

Quoted 28 lines · Dave Chinner
Quoted 27 lines · Brian Foster
Quoted 1 line · Dave Chinner

On Mon, Aug 27, 2012 at 03:51:49PM -0400, Brian Foster wrote:

...

Quoted 23 lines
Quoted 9 lines
fs/xfs/xfs_sync.c +9 -0
+/*
+ * Handle an EOFBLOCKS tagged inode. If this is a forced scan, we wait on the
+ * iolock ourselves rather than rely on the trylock in xfs_free_eofblocks(). 
+ *
+ * We rely on the output parameter from xfs_free_eofblocks() to determine
+ * whether we should clear the tag because in the trylock case, it could have
+ * skipped the inode due to lock contention.
+ */
+STATIC int

fs/xfs/xfs_sync.c xfs_reclaim_inodes_count( · line 992

Quoted 10 lines
fs/xfs/xfs_sync.c +10 -0
+xfs_inode_free_eofblocks(
+	struct xfs_inode	*ip,
+	int			flags)
+{
+	int ret = 0;
+	bool freed = false;
+	bool wait_iolock = (flags & EOFBLOCKS_WAIT) ? true : false;
+
+	if (wait_iolock)
+		xfs_ilock(ip, XFS_IOLOCK_EXCL);

Why do we need the IO lock here? xfs_free_eofblocks() does all the necessary locking....

Quoted 5 lines
Quoted 2 lines

This was for the wait case (e.g., xfs_free_eofblocks() does a trylock on the IO lock and we want to wait for the lock in this case).

Does it really matter if we can't get the iolock and skip the inode?

Well I thought that was the point of the force/wait scan as opposed to the best effort scan. But...

Quoted 10 lines

Note that xfs_inode_ag_walk() will keep a skipped count if you return EAGAIN, and re-run the walk on the AG if any inodes are skipped. This is done to prevent blocking on locks slowing down the scan - instead of blocking we move on to the next inode, and revist the blocked inodes again in a later pass.

IOWs, if you convert the code to use the generic walk and make xfs_free_eofblocks() return EAGAIN when it can't get the io lock, you get the retry for free...

This sounds more efficient. ;) I'll move it to this approach. Thanks.

Brian

Quoted 4 lines

Cheers,

Dave.

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#8 lore raw

RFC PATCH 3/4 xfs: add FREE_EOFBLOCKS ioctl

In reply to Brian Foster

The XFS_IOC_FREE_EOFBLOCKS ioctl allows users to invoke an EOFBLOCKS scan. The xfs_eofblocks structure is defined to support the command parameters (quota type/id and minimum file size).

---
 fs/xfs/xfs_fs.h       |   10 ++++++++++
 fs/xfs/xfs_ioctl.c    |   25 +++++++++++++++++++++++++
 fs/xfs/xfs_quota.h    |    1 +
 fs/xfs/xfs_quotaops.c |    2 +-
 4 files changed, 37 insertions(+), 1 deletions(-)
fs/xfs/xfs_fs.h +10 -0
index c13fed8..6f93db9 100644
@@ -339,6 +339,15 @@ typedef struct xfs_error_injection {
 
 
 /*
+ * Speculative preallocation trimming.
+ */
+typedef struct xfs_eofblocks {
+	__u32		id;		/* quota id */
+	__u32		qtype;		/* quota type */
+	__u64		min_file_size;	/* minimum file size */
+} xfs_eofblocks_t;
+
+/*
  * The user-level Handle Request interface structure.
  */
 typedef struct xfs_fsop_handlereq {
@@ -456,6 +465,7 @@ typedef struct xfs_handle {
 /*	XFS_IOC_GETBIOSIZE ---- deprecated 47	   */
 #define XFS_IOC_GETBMAPX	_IOWR('X', 56, struct getbmap)
 #define XFS_IOC_ZERO_RANGE	_IOW ('X', 57, struct xfs_flock64)
+#define XFS_IOC_FREE_EOFBLOCKS	_IOR ('X', 58, struct xfs_eofblocks)
 
 /*
  * ioctl commands that replace IRIX syssgi()'s
fs/xfs/xfs_ioctl.c +25 -0
index 0e0232c..b91cbcd 100644
@@ -1602,6 +1602,31 @@ xfs_file_ioctl(
 		error = xfs_errortag_clearall(mp, 1);
 		return -error;
 
+	case XFS_IOC_FREE_EOFBLOCKS: {
+		struct xfs_eofblocks eofb;
+		int qtype;
+
+		if (copy_from_user(&eofb, arg, sizeof(eofb)))
+			return -XFS_ERROR(EFAULT);
+
+		qtype = xfs_quota_type(eofb.qtype);
+
+		/*
+		 * TODO: The filtering code currently uses the id in the inode.
+		 * Therefore, I don't think it really matters whether the
+		 * particular quota type is enabled (and the dquot is attached).
+		 *
+		 * Alternatively, we could filter by dquot type. This would
+		 * mean we might have to make sure dquot's are attached during
+		 * the scan and that the particular quota type is enabled. I'm
+		 * not sure that this buys us anything.
+		 */
+
+		/* TODO: might want to just use the eofb structure here */
+		error = xfs_inodes_free_eofblocks(mp, qtype, eofb.id, eofb.min_file_size, EOFBLOCKS_WAIT);
+		return -error;
+	}
+
 	default:
 		return -ENOTTY;
 	}
fs/xfs/xfs_quota.h +1 -0
index b50ec5b..3ec6224 100644
@@ -379,6 +379,7 @@ static inline int xfs_trans_reserve_quota_bydquots(struct xfs_trans *tp,
 extern int xfs_qm_dqcheck(struct xfs_mount *, xfs_disk_dquot_t *,
 				xfs_dqid_t, uint, uint, char *);
 extern int xfs_mount_reset_sbqflags(struct xfs_mount *);
+int xfs_quota_type(int);
 
 #endif	/* __KERNEL__ */
 #endif	/* __XFS_QUOTA_H__ */
fs/xfs/xfs_quotaops.c +1 -1
index fed504f..fe15aa7 100644
@@ -28,7 +28,7 @@
 #include <linux/quota.h>
 
 
-STATIC int
+int
 xfs_quota_type(int type)
 {
 	switch (type) {
Signature · 6 lines
1.7.7.6

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#9 lore raw

Re: [RFC PATCH 3/4] xfs: add FREE_EOFBLOCKS ioctl

Quoted 17 lines · Brian Foster

The XFS_IOC_FREE_EOFBLOCKS ioctl allows users to invoke an EOFBLOCKS scan. The xfs_eofblocks structure is defined to support the command parameters (quota type/id and minimum file size).

---
 fs/xfs/xfs_fs.h       |   10 ++++++++++
 fs/xfs/xfs_ioctl.c    |   25 +++++++++++++++++++++++++
 fs/xfs/xfs_quota.h    |    1 +
 fs/xfs/xfs_quotaops.c |    2 +-
 4 files changed, 37 insertions(+), 1 deletions(-)
fs/xfs/xfs_fs.h +0 -0
index c13fed8..6f93db9 100644

fs/xfs/xfs_fs.h typedef struct xfs_error_injection { · line 348

Quoted 10 lines
fs/xfs/xfs_fs.h +7 -0
@@ -339,6 +339,15 @@ typedef struct xfs_error_injection {
 
 
 /*
+ * Speculative preallocation trimming.
+ */
+typedef struct xfs_eofblocks {
+	__u32		id;		/* quota id */
+	__u32		qtype;		/* quota type */
+	__u64		min_file_size;	/* minimum file size */
+} xfs_eofblocks_t;

No typedefs.

Additionally: this is a user facing API for functionality that has wider potential use than quota. For example, xfs_fsr runs out of space in an AG, but it really wants to put a file there, so it calls XFS_IOC_FREE_EOFBLOCKS to specify the AG it wants all the prealloc removed from.

So, the structure needs a version number so the kernel knows what fields the application is aware of, a flags field to say what operation is being done (i.e. flush by projid) rather than a "qtype" and a bunch of padding so that we can add new elements to it as need arises without breaking existing user binaries.

Quoted 18 lines
fs/xfs/xfs_fs.h +0 -0
  * ioctl commands that replace IRIX syssgi()'s
fs/xfs/xfs_ioctl.c +9 -0
index 0e0232c..b91cbcd 100644
@@ -1602,6 +1602,31 @@ xfs_file_ioctl(
 		error = xfs_errortag_clearall(mp, 1);
 		return -error;
 
+	case XFS_IOC_FREE_EOFBLOCKS: {
+		struct xfs_eofblocks eofb;
+		int qtype;
+
+		if (copy_from_user(&eofb, arg, sizeof(eofb)))
+			return -XFS_ERROR(EFAULT);
+
+		qtype = xfs_quota_type(eofb.qtype);
+

fs/xfs/xfs_ioctl.c xfs_file_ioctl( · line 1623

Quoted 10 lines
fs/xfs/xfs_ioctl.c +10 -0
+		/*
+		 * TODO: The filtering code currently uses the id in the inode.
+		 * Therefore, I don't think it really matters whether the
+		 * particular quota type is enabled (and the dquot is attached).
+		 *
+		 * Alternatively, we could filter by dquot type. This would
+		 * mean we might have to make sure dquot's are attached during
+		 * the scan and that the particular quota type is enabled. I'm
+		 * not sure that this buys us anything.
+		 */

If quota is not enabled, then a request to free blocks determined by a quota ID is invalid and should be rejected. It's a user API - what is and isn't supported needs to be written down in black and white (i.e. in the xfsctl man page).

fs/xfs/xfs_ioctl.c xfs_file_ioctl( · line 1626

Quoted 2 lines
fs/xfs/xfs_ioctl.c +2 -0
+		/* TODO: might want to just use the eofb structure here */
+		error = xfs_inodes_free_eofblocks(mp, qtype, eofb.id, eofb.min_file_size, EOFBLOCKS_WAIT);

Yes, just pass the structure - it means it can be passed all the way down to the execute function, and only that code needs to handle different versions of the structure.

Cheers,

Dave.

Signature · 7 lines
Dave Chinner
[email protected]

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#10 lore raw

Re: [RFC PATCH 3/4] xfs: add FREE_EOFBLOCKS ioctl

Quoted 20 lines · Dave Chinner
Quoted 19 lines · Brian Foster

The XFS_IOC_FREE_EOFBLOCKS ioctl allows users to invoke an EOFBLOCKS scan. The xfs_eofblocks structure is defined to support the command parameters (quota type/id and minimum file size).

---
 fs/xfs/xfs_fs.h       |   10 ++++++++++
 fs/xfs/xfs_ioctl.c    |   25 +++++++++++++++++++++++++
 fs/xfs/xfs_quota.h    |    1 +
 fs/xfs/xfs_quotaops.c |    2 +-
 4 files changed, 37 insertions(+), 1 deletions(-)
fs/xfs/xfs_fs.h +0 -0
index c13fed8..6f93db9 100644
@@ -339,6 +339,15 @@ typedef struct xfs_error_injection {
 
 
Quoted 11 lines

fs/xfs/xfs_fs.h typedef struct xfs_error_injection { · line 348

Quoted 8 lines
fs/xfs/xfs_fs.h +7 -0
 /*
+ * Speculative preallocation trimming.
+ */
+typedef struct xfs_eofblocks {
+	__u32		id;		/* quota id */
+	__u32		qtype;		/* quota type */
+	__u64		min_file_size;	/* minimum file size */
+} xfs_eofblocks_t;

No typedefs.

This is something I see throughout the code that I haven't quite followed (i.e., using the _t typedefs vs. not). Is the general consensus to move away from typedefs when possible?

...

Quoted 16 lines
Quoted 16 lines
fs/xfs/xfs_fs.h +16 -0
+	case XFS_IOC_FREE_EOFBLOCKS: {
+		struct xfs_eofblocks eofb;
+		int qtype;
+
+		if (copy_from_user(&eofb, arg, sizeof(eofb)))
+			return -XFS_ERROR(EFAULT);
+
+		qtype = xfs_quota_type(eofb.qtype);
+
+		/*
+		 * TODO: The filtering code currently uses the id in the inode.
+		 * Therefore, I don't think it really matters whether the
+		 * particular quota type is enabled (and the dquot is attached).
+		 *
+		 * Alternatively, we could filter by dquot type. This would
+		 * mean we might have to make sure dquot's are attached during
Quoted 9 lines

fs/xfs/xfs_fs.h

Quoted 3 lines
fs/xfs/xfs_fs.h +3 -0
+		 * the scan and that the particular quota type is enabled. I'm
+		 * not sure that this buys us anything.
+		 */

If quota is not enabled, then a request to free blocks determined by a quota ID is invalid and should be rejected. It's a user API - what is and isn't supported needs to be written down in black and white (i.e. in the xfsctl man page).

Ok. I was thinking that we could support the ability to scan by uid/gid regardless of whether quota is enabled, but perhaps there's no purpose to that if a quota isn't enabled.

Brian

Quoted 11 lines

fs/xfs/xfs_fs.h

Quoted 2 lines
fs/xfs/xfs_fs.h +2 -0
+		/* TODO: might want to just use the eofb structure here */
+		error = xfs_inodes_free_eofblocks(mp, qtype, eofb.id, eofb.min_file_size, EOFBLOCKS_WAIT);

Yes, just pass the structure - it means it can be passed all the way down to the execute function, and only that code needs to handle different versions of the structure.

Cheers,

Dave.

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#11 lore raw

Re: [RFC PATCH 3/4] xfs: add FREE_EOFBLOCKS ioctl

Quoted 33 lines · Brian Foster
Quoted 20 lines · Dave Chinner
Quoted 19 lines · Brian Foster

The XFS_IOC_FREE_EOFBLOCKS ioctl allows users to invoke an EOFBLOCKS scan. The xfs_eofblocks structure is defined to support the command parameters (quota type/id and minimum file size).

---
 fs/xfs/xfs_fs.h       |   10 ++++++++++
 fs/xfs/xfs_ioctl.c    |   25 +++++++++++++++++++++++++
 fs/xfs/xfs_quota.h    |    1 +
 fs/xfs/xfs_quotaops.c |    2 +-
 4 files changed, 37 insertions(+), 1 deletions(-)
fs/xfs/xfs_fs.h +0 -0
index c13fed8..6f93db9 100644
@@ -339,6 +339,15 @@ typedef struct xfs_error_injection {
 
 
Quoted 11 lines

fs/xfs/xfs_fs.h typedef struct xfs_error_injection { · line 348

Quoted 8 lines
fs/xfs/xfs_fs.h +7 -0
 /*
+ * Speculative preallocation trimming.
+ */
+typedef struct xfs_eofblocks {
+	__u32		id;		/* quota id */
+	__u32		qtype;		/* quota type */
+	__u64		min_file_size;	/* minimum file size */
+} xfs_eofblocks_t;

No typedefs.

Quoted 3 lines

This is something I see throughout the code that I haven't quite followed (i.e., using the _t typedefs vs. not). Is the general consensus to move away from typedefs when possible?

Yes. The Irix code that XFS came from was full of typedefs - part of it was to try to strictly type check things that were the same storage size or on-disk vs in-memory. We've got other ways of doing that better (e.g. the endian checking sparse does), and typedefs are generally frowned upon in the main kernel code because they often obfuscate the code rather than improve it, so we're removing them as we modify code or write new code.

Quoted 18 lines
Quoted 8 lines
Quoted 8 lines
fs/xfs/xfs_fs.h +8 -0
+
+		/*
+		 * TODO: The filtering code currently uses the id in the inode.
+		 * Therefore, I don't think it really matters whether the
+		 * particular quota type is enabled (and the dquot is attached).
+		 *
+		 * Alternatively, we could filter by dquot type. This would
+		 * mean we might have to make sure dquot's are attached during
Quoted 9 lines

fs/xfs/xfs_fs.h

Quoted 3 lines
fs/xfs/xfs_fs.h +3 -0
+		 * the scan and that the particular quota type is enabled. I'm
+		 * not sure that this buys us anything.
+		 */

If quota is not enabled, then a request to free blocks determined by a quota ID is invalid and should be rejected. It's a user API - what is and isn't supported needs to be written down in black and white (i.e. in the xfsctl man page).

Quoted 3 lines

Ok. I was thinking that we could support the ability to scan by uid/gid regardless of whether quota is enabled, but perhaps there's no purpose to that if a quota isn't enabled.

I can't really think of a use case for doing this. Making the API more expansive in future if someone needs this can be done - it's removing stuff that is really hard to do. Hence, don't add it if it is not going to be used immediately. :)

Cheers,

Dave.

Signature · 7 lines
Dave Chinner
[email protected]

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#12 lore raw

Re: [RFC PATCH 3/4] xfs: add FREE_EOFBLOCKS ioctl

Quoted 3 lines · Dave Chinner
Quoted 2 lines · Brian Foster
Quoted 1 line · Dave Chinner

On Mon, Aug 27, 2012 at 03:51:50PM -0400, Brian Foster wrote:

...

Quoted 4 lines
Quoted 3 lines

This is something I see throughout the code that I haven't quite followed (i.e., using the _t typedefs vs. not). Is the general consensus to move away from typedefs when possible?

Quoted 8 lines

Yes. The Irix code that XFS came from was full of typedefs - part of it was to try to strictly type check things that were the same storage size or on-disk vs in-memory. We've got other ways of doing that better (e.g. the endian checking sparse does), and typedefs are generally frowned upon in the main kernel code because they often obfuscate the code rather than improve it, so we're removing them as we modify code or write new code.

Good to know.

...

Quoted 9 lines
Quoted 3 lines

Ok. I was thinking that we could support the ability to scan by uid/gid regardless of whether quota is enabled, but perhaps there's no purpose to that if a quota isn't enabled.

I can't really think of a use case for doing this. Making the API more expansive in future if someone needs this can be done - it's removing stuff that is really hard to do. Hence, don't add it if it is not going to be used immediately. :)

Ok.

Brian

Quoted 4 lines

Cheers,

Dave.

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#13 lore raw

RFC PATCH 4/4 xfs: add background scanning to clear EOFBLOCKS inodes

In reply to Brian Foster

Create a delayed_work to enable background scanning and freeing of EOFBLOCKS inodes. The scanner kicks in once speculative preallocation occurs and stops requeueing itself when no EOFBLOCKS inodes exist.

Scans are queued on the existing syncd workqueue and the interval is tied to the syncd interval, which is a default of 30s. The minimum file size for a background scan is hardcoded to 100MB.

---
 fs/xfs/xfs_mount.h |    2 ++
 fs/xfs/xfs_sync.c  |   33 +++++++++++++++++++++++++++++++++
 fs/xfs/xfs_sync.h  |    3 +++
 3 files changed, 38 insertions(+), 0 deletions(-)
fs/xfs/xfs_mount.h +2 -0
index 05a05a7..a966bf4 100644
@@ -204,6 +204,8 @@ typedef struct xfs_mount {
 	struct xfs_mru_cache	*m_filestream;  /* per-mount filestream data */
 	struct delayed_work	m_sync_work;	/* background sync work */
 	struct delayed_work	m_reclaim_work;	/* background inode reclaim */
+	struct delayed_work	m_eofblocks_work; /* background eof blocks
+						     trimming */
 	struct work_struct	m_flush_work;	/* background inode flush */
 	__int64_t		m_update_flags;	/* sb flags we need to update
 						   on the next remount,rw */
fs/xfs/xfs_sync.c +33 -0
index 27c3c46..bd1ca90 100644
@@ -489,6 +489,34 @@ xfs_flush_worker(
 	xfs_sync_data(mp, SYNC_TRYLOCK | SYNC_WAIT);
 }
 
+/*
+ * Background scanning to trim post-EOF preallocated space. This is queued
+ * based on the syncd tunable (30s by default).
+ *
+ * TODO: Do we want an independent tunable (and default.. off by default)?
+ */
+STATIC void
+xfs_queue_eofblocks(
+	struct xfs_mount *mp)
+{
+	rcu_read_lock();
+	if (radix_tree_tagged(&mp->m_perag_tree, XFS_ICI_EOFBLOCKS_TAG))
+		queue_delayed_work(xfs_syncd_wq, &mp->m_eofblocks_work,
+			msecs_to_jiffies(xfs_syncd_centisecs * 10));
+	rcu_read_unlock();
+}
+
+STATIC void
+xfs_eofblocks_worker(
+	struct work_struct *work)
+{
+	struct xfs_mount *mp = container_of(to_delayed_work(work),
+				struct xfs_mount, m_eofblocks_work);
+	xfs_inodes_free_eofblocks(mp, XFS_DQ_PROJ, XFS_PROJID_DEFAULT,
+		XFS_EOFBLOCKS_DEFAULT_MINFILESIZE, 0);
+	xfs_queue_eofblocks(mp);
+}
+
 int
 xfs_syncd_init(
 	struct xfs_mount	*mp)
@@ -496,6 +524,7 @@ xfs_syncd_init(
 	INIT_WORK(&mp->m_flush_work, xfs_flush_worker);
 	INIT_DELAYED_WORK(&mp->m_sync_work, xfs_sync_worker);
 	INIT_DELAYED_WORK(&mp->m_reclaim_work, xfs_reclaim_worker);
+	INIT_DELAYED_WORK(&mp->m_eofblocks_work, xfs_eofblocks_worker);
 
 	xfs_syncd_queue_sync(mp);
 
@@ -508,6 +537,7 @@ xfs_syncd_stop(
 {
 	cancel_delayed_work_sync(&mp->m_sync_work);
 	cancel_delayed_work_sync(&mp->m_reclaim_work);
+	cancel_delayed_work_sync(&mp->m_eofblocks_work);
 	cancel_work_sync(&mp->m_flush_work);
 }
 
@@ -1159,6 +1189,9 @@ __xfs_inode_set_eofblocks_tag(
 				   XFS_ICI_EOFBLOCKS_TAG);
 		spin_unlock(&ip->i_mount->m_perag_lock);
 
+		/* kick off background trimming */
+		xfs_queue_eofblocks(ip->i_mount);
+
 		trace_xfs_perag_set_eofblocks(ip->i_mount, pag->pag_agno,
 					      -1, _RET_IP_);
 	}
fs/xfs/xfs_sync.h +3 -0
index 78aca41..3ce8c3f 100644
@@ -45,6 +45,9 @@ void __xfs_inode_clear_reclaim_tag(struct xfs_mount *mp, struct xfs_perag *pag,
 
 #define EOFBLOCKS_WAIT		0x0001
 
+/* TODO: should we have a tunable? */
+#define XFS_EOFBLOCKS_DEFAULT_MINFILESIZE 1024 * 1024 * 100
+
 void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
 void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
 int xfs_inodes_free_eofblocks(struct xfs_mount *, int, uint32_t, uint64_t, int);
Signature · 6 lines
1.7.7.6

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#14 lore raw

Re: [RFC PATCH 4/4] xfs: add background scanning to clear EOFBLOCKS inodes

Quoted 8 lines · Brian Foster

Create a delayed_work to enable background scanning and freeing of EOFBLOCKS inodes. The scanner kicks in once speculative preallocation occurs and stops requeueing itself when no EOFBLOCKS inodes exist.

Scans are queued on the existing syncd workqueue and the interval is tied to the syncd interval, which is a default of 30s. The minimum file size for a background scan is hardcoded to 100MB.

I'd set it to be much longer than the xfs_syncd_centisecs period. The data may not be written back for a long while, and a file that is open, written and closed repeatedly (NFS write patterns for large and/or slowly written files) should not have it's preallocation truncated every 30s.

I'd suggest that a 5 minute sweep is probably sufficiently frequent to avoid most fragmentation issues and long term buildup of speculative preallocation whilst not perturbing large/slow writers too much....

Apart from that, there isn't anything wrong with the code, except I'll note again that it conflicts with my syncd removal patchset....

Cheers,

Dave.

Signature · 7 lines
Dave Chinner
[email protected]

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#15 lore raw

Re: [RFC PATCH 4/4] xfs: add background scanning to clear EOFBLOCKS inodes

Quoted 16 lines · Dave Chinner
Quoted 8 lines · Brian Foster

Create a delayed_work to enable background scanning and freeing of EOFBLOCKS inodes. The scanner kicks in once speculative preallocation occurs and stops requeueing itself when no EOFBLOCKS inodes exist.

Scans are queued on the existing syncd workqueue and the interval is tied to the syncd interval, which is a default of 30s. The minimum file size for a background scan is hardcoded to 100MB.

I'd set it to be much longer than the xfs_syncd_centisecs period. The data may not be written back for a long while, and a file that is open, written and closed repeatedly (NFS write patterns for large and/or slowly written files) should not have it's preallocation truncated every 30s.

Quoted 4 lines

I'd suggest that a 5 minute sweep is probably sufficiently frequent to avoid most fragmentation issues and long term buildup of speculative preallocation whilst not perturbing large/slow writers too much....

Any thoughts on having tunables for both values (time and min size?) on the background scanning?

Quoted 3 lines

Apart from that, there isn't anything wrong with the code, except I'll note again that it conflicts with my syncd removal patchset....

Yeah, I skimmed through those patches and noticed it would shuffle this code around. Thanks for pointing it out. I'll keep an eye on that set.

Thanks for the review. I'll follow up with a patchset that incorporates the feedback.

Brian

Quoted 4 lines

Cheers,

Dave.

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#16 lore raw

Re: [RFC PATCH 4/4] xfs: add background scanning to clear EOFBLOCKS inodes

Quoted 17 lines · Brian Foster
Quoted 16 lines · Dave Chinner
Quoted 8 lines · Brian Foster

Create a delayed_work to enable background scanning and freeing of EOFBLOCKS inodes. The scanner kicks in once speculative preallocation occurs and stops requeueing itself when no EOFBLOCKS inodes exist.

Scans are queued on the existing syncd workqueue and the interval is tied to the syncd interval, which is a default of 30s. The minimum file size for a background scan is hardcoded to 100MB.

I'd set it to be much longer than the xfs_syncd_centisecs period. The data may not be written back for a long while, and a file that is open, written and closed repeatedly (NFS write patterns for large and/or slowly written files) should not have it's preallocation truncated every 30s.

Quoted 7 lines
Quoted 4 lines

I'd suggest that a 5 minute sweep is probably sufficiently frequent to avoid most fragmentation issues and long term buildup of speculative preallocation whilst not perturbing large/slow writers too much....

Any thoughts on having tunables for both values (time and min size?) on the background scanning?

Well, my suggestion for timing is as per above (xfs_syncd_centisecs
* 100), but I don't really have any good rule of thumb for the minimum size. What threshold do people start to notice this?

I'd SWAG that something like 32MB is a good size to start at because most IO subsystems will still be able to reach full bandwidth with extents of this size when reading files.

Alternatively, if you can determine if the inode is still in use at the time of the scan (e.g. elevated reference count due to an open fd) and skip the truncation for those inodes, then a minimum size is not really needed, right?

Cheers,

Dave.

Signature · 7 lines
Dave Chinner
[email protected]

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#17 lore raw

Re: [RFC PATCH 4/4] xfs: add background scanning to clear EOFBLOCKS inodes

Quoted 3 lines · Dave Chinner
Quoted 2 lines · Brian Foster
Quoted 1 line · Dave Chinner

On Mon, Aug 27, 2012 at 03:51:51PM -0400, Brian Foster wrote:

...

Quoted 8 lines
Quoted 3 lines

Any thoughts on having tunables for both values (time and min size?) on the background scanning?

Well, my suggestion for timing is as per above (xfs_syncd_centisecs
* 100), but I don't really have any good rule of thumb for the minimum size. What threshold do people start to notice this?

For the testing I've done so far, I'm hitting EDQUOT with 20-30GB of space left while sequentially writing to many large files. I'm really just trying to get used space before failure more in the ball park of the limit, so I'm not going to complain too much over leaving a few hundred MB or so around on an otherwise full quota. ;) From where I sit, the problem is more when we extend a file by 2, 4, 8GB and consume a large amount of limited available space.

I suppose for the background scanning, it's more about just using a value that doesn't get in the way of general behavior/performance. I'll do some more testing in this area.

Quoted 9 lines

I'd SWAG that something like 32MB is a good size to start at because most IO subsystems will still be able to reach full bandwidth with extents of this size when reading files.

Alternatively, if you can determine if the inode is still in use at the time of the scan (e.g. elevated reference count due to an open fd) and skip the truncation for those inodes, then a minimum size is not really needed, right?

Hmm, good idea. Though perhaps I can use the min_size as a force parameter (i.e., trim anything over this size), and the inode in use check allows a more conservative default. I'll have to play around with the right time/size values some more to get a better feel for it. I'll probably include tunables at least for testing purposes, and they can always be removed later. Thanks.

Brian

Quoted 4 lines

Cheers,

Dave.

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#18 lore raw

Re: [RFC PATCH 4/4] xfs: add background scanning to clear EOFBLOCKS inodes

Quoted 9 lines · Brian Foster
Quoted 3 lines · Dave Chinner
Quoted 2 lines · Brian Foster
Quoted 1 line · Dave Chinner

On Mon, Aug 27, 2012 at 03:51:51PM -0400, Brian Foster wrote:

...

Quoted 4 lines
Quoted 3 lines

Any thoughts on having tunables for both values (time and min size?) on the background scanning?

Quoted 7 lines
Quoted 4 lines

Well, my suggestion for timing is as per above (xfs_syncd_centisecs
* 100), but I don't really have any good rule of thumb for the minimum size. What threshold do people start to notice this?

For the testing I've done so far, I'm hitting EDQUOT with 20-30GB of space left while sequentially writing to many large files.

Sure, background scanning won't prevent that, though. The background scan is to catch preallocation that is no longer needed. i.e. the files are no longer being written and have no dirty date but due to the access nature, xfs_release() didn't free the unused preallocation. The background scan will clean that up faster than waiting for the inodes to cycle through the cache....

Quoted 3 lines

I'm really
just trying to get used space before failure more in the ball park of the limit,

That's what prealloc size throttling is for. ;)

Quoted 8 lines

so I'm not going to complain too much over leaving a few hundred MB or so around on an otherwise full quota. ;) From where I sit, the problem is more when we extend a file by 2, 4, 8GB and consume a large amount of limited available space.

I suppose for the background scanning, it's more about just using a value that doesn't get in the way of general behavior/performance. I'll do some more testing in this area.

Right.

Quoted 4 lines
Quoted 4 lines

I'd SWAG that something like 32MB is a good size to start at because most IO subsystems will still be able to reach full bandwidth with extents of this size when reading files.

Quoted 8 lines
Quoted 5 lines

Alternatively, if you can determine if the inode is still in use at the time of the scan (e.g. elevated reference count due to an open fd) and skip the truncation for those inodes, then a minimum size is not really needed, right?

Hmm, good idea. Though perhaps I can use the min_size as a force parameter (i.e., trim anything over this size),

If it's a background scan, we don't want to trim active preallocations.

Quoted 2 lines

and the inode in use
check allows a more conservative default.

I just thought of a better check than an in-use check - if the inode has a dirty page cache, don't trim it as the speculative prealloc is still useful. If the inode is clean, it has not recently been written to so we can remove the speculative prealloc and we are unlikely to suffer any penalty from doing so....

Cheers,

Dave.

Signature · 7 lines
Dave Chinner
[email protected]

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs