Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e0ac6d45 authored by Dave Chinner's avatar Dave Chinner
Browse files

Merge branch 'xfs-quota-eofblocks-scan' into for-next

parents 6eee8972 f074051f
Loading
Loading
Loading
Loading
+15 −0
Original line number Original line Diff line number Diff line
@@ -139,6 +139,21 @@ static inline xfs_dquot_t *xfs_inode_dquot(struct xfs_inode *ip, int type)
	}
	}
}
}


/*
 * Check whether a dquot is under low free space conditions. We assume the quota
 * is enabled and enforced.
 */
static inline bool xfs_dquot_lowsp(struct xfs_dquot *dqp)
{
	int64_t freesp;

	freesp = be64_to_cpu(dqp->q_core.d_blk_hardlimit) - dqp->q_res_bcount;
	if (freesp < dqp->q_low_space[XFS_QLOWSP_1_PCNT])
		return true;

	return false;
}

#define XFS_DQ_IS_LOCKED(dqp)	(mutex_is_locked(&((dqp)->q_qlock)))
#define XFS_DQ_IS_LOCKED(dqp)	(mutex_is_locked(&((dqp)->q_qlock)))
#define XFS_DQ_IS_DIRTY(dqp)	((dqp)->dq_flags & XFS_DQ_DIRTY)
#define XFS_DQ_IS_DIRTY(dqp)	((dqp)->dq_flags & XFS_DQ_DIRTY)
#define XFS_QM_ISUDQ(dqp)	((dqp)->dq_flags & XFS_DQ_USER)
#define XFS_QM_ISUDQ(dqp)	((dqp)->dq_flags & XFS_DQ_USER)
+19 −4
Original line number Original line Diff line number Diff line
@@ -38,6 +38,7 @@
#include "xfs_trace.h"
#include "xfs_trace.h"
#include "xfs_log.h"
#include "xfs_log.h"
#include "xfs_dinode.h"
#include "xfs_dinode.h"
#include "xfs_icache.h"


#include <linux/aio.h>
#include <linux/aio.h>
#include <linux/dcache.h>
#include <linux/dcache.h>
@@ -689,14 +690,28 @@ write_retry:
	ret = generic_perform_write(file, from, pos);
	ret = generic_perform_write(file, from, pos);
	if (likely(ret >= 0))
	if (likely(ret >= 0))
		iocb->ki_pos = pos + ret;
		iocb->ki_pos = pos + ret;

	/*
	/*
	 * If we just got an ENOSPC, try to write back all dirty inodes to
	 * If we hit a space limit, try to free up some lingering preallocated
	 * convert delalloc space to free up some of the excess reserved
	 * space before returning an error. In the case of ENOSPC, first try to
	 * metadata space.
	 * write back all dirty inodes to free up some of the excess reserved
	 * metadata space. This reduces the chances that the eofblocks scan
	 * waits on dirty mappings. Since xfs_flush_inodes() is serialized, this
	 * also behaves as a filter to prevent too many eofblocks scans from
	 * running at the same time.
	 */
	 */
	if (ret == -ENOSPC && !enospc) {
	if (ret == -EDQUOT && !enospc) {
		enospc = xfs_inode_free_quota_eofblocks(ip);
		if (enospc)
			goto write_retry;
	} else if (ret == -ENOSPC && !enospc) {
		struct xfs_eofblocks eofb = {0};

		enospc = 1;
		enospc = 1;
		xfs_flush_inodes(ip->i_mount);
		xfs_flush_inodes(ip->i_mount);
		eofb.eof_scan_owner = ip->i_ino; /* for locking */
		eofb.eof_flags = XFS_EOF_FLAGS_SYNC;
		xfs_icache_free_eofblocks(ip->i_mount, &eofb);
		goto write_retry;
		goto write_retry;
	}
	}


+3 −0
Original line number Original line Diff line number Diff line
@@ -375,6 +375,9 @@ struct xfs_fs_eofblocks {
#define XFS_EOF_FLAGS_GID		(1 << 2) /* filter by gid */
#define XFS_EOF_FLAGS_GID		(1 << 2) /* filter by gid */
#define XFS_EOF_FLAGS_PRID		(1 << 3) /* filter by project id */
#define XFS_EOF_FLAGS_PRID		(1 << 3) /* filter by project id */
#define XFS_EOF_FLAGS_MINFILESIZE	(1 << 4) /* filter by min file size */
#define XFS_EOF_FLAGS_MINFILESIZE	(1 << 4) /* filter by min file size */
#define XFS_EOF_FLAGS_UNION		(1 << 5) /* union filter algorithm;
						  * kernel only, not included in
						  * valid mask */
#define XFS_EOF_FLAGS_VALID	\
#define XFS_EOF_FLAGS_VALID	\
	(XFS_EOF_FLAGS_SYNC |	\
	(XFS_EOF_FLAGS_SYNC |	\
	 XFS_EOF_FLAGS_UID |	\
	 XFS_EOF_FLAGS_UID |	\
+94 −2
Original line number Original line Diff line number Diff line
@@ -33,6 +33,9 @@
#include "xfs_trace.h"
#include "xfs_trace.h"
#include "xfs_icache.h"
#include "xfs_icache.h"
#include "xfs_bmap_util.h"
#include "xfs_bmap_util.h"
#include "xfs_quota.h"
#include "xfs_dquot_item.h"
#include "xfs_dquot.h"


#include <linux/kthread.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/freezer.h>
@@ -1203,6 +1206,30 @@ xfs_inode_match_id(
	return 1;
	return 1;
}
}


/*
 * A union-based inode filtering algorithm. Process the inode if any of the
 * criteria match. This is for global/internal scans only.
 */
STATIC int
xfs_inode_match_id_union(
	struct xfs_inode	*ip,
	struct xfs_eofblocks	*eofb)
{
	if ((eofb->eof_flags & XFS_EOF_FLAGS_UID) &&
	    uid_eq(VFS_I(ip)->i_uid, eofb->eof_uid))
		return 1;

	if ((eofb->eof_flags & XFS_EOF_FLAGS_GID) &&
	    gid_eq(VFS_I(ip)->i_gid, eofb->eof_gid))
		return 1;

	if ((eofb->eof_flags & XFS_EOF_FLAGS_PRID) &&
	    xfs_get_projid(ip) == eofb->eof_prid)
		return 1;

	return 0;
}

STATIC int
STATIC int
xfs_inode_free_eofblocks(
xfs_inode_free_eofblocks(
	struct xfs_inode	*ip,
	struct xfs_inode	*ip,
@@ -1211,6 +1238,10 @@ xfs_inode_free_eofblocks(
{
{
	int ret;
	int ret;
	struct xfs_eofblocks *eofb = args;
	struct xfs_eofblocks *eofb = args;
	bool need_iolock = true;
	int match;

	ASSERT(!eofb || (eofb && eofb->eof_scan_owner != 0));


	if (!xfs_can_free_eofblocks(ip, false)) {
	if (!xfs_can_free_eofblocks(ip, false)) {
		/* inode could be preallocated or append-only */
		/* inode could be preallocated or append-only */
@@ -1228,16 +1259,28 @@ xfs_inode_free_eofblocks(
		return 0;
		return 0;


	if (eofb) {
	if (eofb) {
		if (!xfs_inode_match_id(ip, eofb))
		if (eofb->eof_flags & XFS_EOF_FLAGS_UNION)
			match = xfs_inode_match_id_union(ip, eofb);
		else
			match = xfs_inode_match_id(ip, eofb);
		if (!match)
			return 0;
			return 0;


		/* skip the inode if the file size is too small */
		/* skip the inode if the file size is too small */
		if (eofb->eof_flags & XFS_EOF_FLAGS_MINFILESIZE &&
		if (eofb->eof_flags & XFS_EOF_FLAGS_MINFILESIZE &&
		    XFS_ISIZE(ip) < eofb->eof_min_file_size)
		    XFS_ISIZE(ip) < eofb->eof_min_file_size)
			return 0;
			return 0;

		/*
		 * A scan owner implies we already hold the iolock. Skip it in
		 * xfs_free_eofblocks() to avoid deadlock. This also eliminates
		 * the possibility of EAGAIN being returned.
		 */
		if (eofb->eof_scan_owner == ip->i_ino)
			need_iolock = false;
	}
	}


	ret = xfs_free_eofblocks(ip->i_mount, ip, true);
	ret = xfs_free_eofblocks(ip->i_mount, ip, need_iolock);


	/* don't revisit the inode if we're not waiting */
	/* don't revisit the inode if we're not waiting */
	if (ret == -EAGAIN && !(flags & SYNC_WAIT))
	if (ret == -EAGAIN && !(flags & SYNC_WAIT))
@@ -1260,6 +1303,55 @@ xfs_icache_free_eofblocks(
					 eofb, XFS_ICI_EOFBLOCKS_TAG);
					 eofb, XFS_ICI_EOFBLOCKS_TAG);
}
}


/*
 * Run eofblocks scans on the quotas applicable to the inode. For inodes with
 * multiple quotas, we don't know exactly which quota caused an allocation
 * failure. We make a best effort by including each quota under low free space
 * conditions (less than 1% free space) in the scan.
 */
int
xfs_inode_free_quota_eofblocks(
	struct xfs_inode *ip)
{
	int scan = 0;
	struct xfs_eofblocks eofb = {0};
	struct xfs_dquot *dq;

	ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));

	/*
	 * Set the scan owner to avoid a potential livelock. Otherwise, the scan
	 * can repeatedly trylock on the inode we're currently processing. We
	 * run a sync scan to increase effectiveness and use the union filter to
	 * cover all applicable quotas in a single scan.
	 */
	eofb.eof_scan_owner = ip->i_ino;
	eofb.eof_flags = XFS_EOF_FLAGS_UNION|XFS_EOF_FLAGS_SYNC;

	if (XFS_IS_UQUOTA_ENFORCED(ip->i_mount)) {
		dq = xfs_inode_dquot(ip, XFS_DQ_USER);
		if (dq && xfs_dquot_lowsp(dq)) {
			eofb.eof_uid = VFS_I(ip)->i_uid;
			eofb.eof_flags |= XFS_EOF_FLAGS_UID;
			scan = 1;
		}
	}

	if (XFS_IS_GQUOTA_ENFORCED(ip->i_mount)) {
		dq = xfs_inode_dquot(ip, XFS_DQ_GROUP);
		if (dq && xfs_dquot_lowsp(dq)) {
			eofb.eof_gid = VFS_I(ip)->i_gid;
			eofb.eof_flags |= XFS_EOF_FLAGS_GID;
			scan = 1;
		}
	}

	if (scan)
		xfs_icache_free_eofblocks(ip->i_mount, &eofb);

	return scan;
}

void
void
xfs_inode_set_eofblocks_tag(
xfs_inode_set_eofblocks_tag(
	xfs_inode_t	*ip)
	xfs_inode_t	*ip)
+3 −0
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@ struct xfs_eofblocks {
	kgid_t		eof_gid;
	kgid_t		eof_gid;
	prid_t		eof_prid;
	prid_t		eof_prid;
	__u64		eof_min_file_size;
	__u64		eof_min_file_size;
	xfs_ino_t	eof_scan_owner;
};
};


#define SYNC_WAIT		0x0001	/* wait for i/o to complete */
#define SYNC_WAIT		0x0001	/* wait for i/o to complete */
@@ -57,6 +58,7 @@ void xfs_inode_set_reclaim_tag(struct xfs_inode *ip);
void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
int xfs_icache_free_eofblocks(struct xfs_mount *, struct xfs_eofblocks *);
int xfs_icache_free_eofblocks(struct xfs_mount *, struct xfs_eofblocks *);
int xfs_inode_free_quota_eofblocks(struct xfs_inode *ip);
void xfs_eofblocks_worker(struct work_struct *);
void xfs_eofblocks_worker(struct work_struct *);


int xfs_inode_ag_iterator(struct xfs_mount *mp,
int xfs_inode_ag_iterator(struct xfs_mount *mp,
@@ -84,6 +86,7 @@ xfs_fs_eofblocks_from_user(
	dst->eof_flags = src->eof_flags;
	dst->eof_flags = src->eof_flags;
	dst->eof_prid = src->eof_prid;
	dst->eof_prid = src->eof_prid;
	dst->eof_min_file_size = src->eof_min_file_size;
	dst->eof_min_file_size = src->eof_min_file_size;
	dst->eof_scan_owner = NULLFSINO;


	dst->eof_uid = INVALID_UID;
	dst->eof_uid = INVALID_UID;
	if (src->eof_flags & XFS_EOF_FLAGS_UID) {
	if (src->eof_flags & XFS_EOF_FLAGS_UID) {
Loading