mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-18 22:34:48 +00:00
Merge branch 'xfs-quota-eofblocks-scan' into for-next
This commit is contained in:
commit
e0ac6d45bc
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 | \
|
||||||
|
@ -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)
|
||||||
|
@ -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) {
|
||||||
|
@ -397,7 +397,8 @@ xfs_quota_calc_throttle(
|
|||||||
struct xfs_inode *ip,
|
struct xfs_inode *ip,
|
||||||
int type,
|
int type,
|
||||||
xfs_fsblock_t *qblocks,
|
xfs_fsblock_t *qblocks,
|
||||||
int *qshift)
|
int *qshift,
|
||||||
|
int64_t *qfreesp)
|
||||||
{
|
{
|
||||||
int64_t freesp;
|
int64_t freesp;
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
@ -406,6 +407,7 @@ xfs_quota_calc_throttle(
|
|||||||
/* over hi wmark, squash the prealloc completely */
|
/* over hi wmark, squash the prealloc completely */
|
||||||
if (dq->q_res_bcount >= dq->q_prealloc_hi_wmark) {
|
if (dq->q_res_bcount >= dq->q_prealloc_hi_wmark) {
|
||||||
*qblocks = 0;
|
*qblocks = 0;
|
||||||
|
*qfreesp = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,6 +420,9 @@ xfs_quota_calc_throttle(
|
|||||||
shift += 2;
|
shift += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (freesp < *qfreesp)
|
||||||
|
*qfreesp = freesp;
|
||||||
|
|
||||||
/* only overwrite the throttle values if we are more aggressive */
|
/* only overwrite the throttle values if we are more aggressive */
|
||||||
if ((freesp >> shift) < (*qblocks >> *qshift)) {
|
if ((freesp >> shift) < (*qblocks >> *qshift)) {
|
||||||
*qblocks = freesp;
|
*qblocks = freesp;
|
||||||
@ -476,15 +481,18 @@ xfs_iomap_prealloc_size(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check each quota to cap the prealloc size and provide a shift
|
* Check each quota to cap the prealloc size, provide a shift value to
|
||||||
* value to throttle with.
|
* throttle with and adjust amount of available space.
|
||||||
*/
|
*/
|
||||||
if (xfs_quota_need_throttle(ip, XFS_DQ_USER, alloc_blocks))
|
if (xfs_quota_need_throttle(ip, XFS_DQ_USER, alloc_blocks))
|
||||||
xfs_quota_calc_throttle(ip, XFS_DQ_USER, &qblocks, &qshift);
|
xfs_quota_calc_throttle(ip, XFS_DQ_USER, &qblocks, &qshift,
|
||||||
|
&freesp);
|
||||||
if (xfs_quota_need_throttle(ip, XFS_DQ_GROUP, alloc_blocks))
|
if (xfs_quota_need_throttle(ip, XFS_DQ_GROUP, alloc_blocks))
|
||||||
xfs_quota_calc_throttle(ip, XFS_DQ_GROUP, &qblocks, &qshift);
|
xfs_quota_calc_throttle(ip, XFS_DQ_GROUP, &qblocks, &qshift,
|
||||||
|
&freesp);
|
||||||
if (xfs_quota_need_throttle(ip, XFS_DQ_PROJ, alloc_blocks))
|
if (xfs_quota_need_throttle(ip, XFS_DQ_PROJ, alloc_blocks))
|
||||||
xfs_quota_calc_throttle(ip, XFS_DQ_PROJ, &qblocks, &qshift);
|
xfs_quota_calc_throttle(ip, XFS_DQ_PROJ, &qblocks, &qshift,
|
||||||
|
&freesp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The final prealloc size is set to the minimum of free space available
|
* The final prealloc size is set to the minimum of free space available
|
||||||
|
Loading…
x
Reference in New Issue
Block a user