Loading fs/xfs/xfs_dquot.h +15 −0 Original line number Original line Diff line number Diff line Loading @@ -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) Loading fs/xfs/xfs_file.c +19 −4 Original line number Original line Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; } } Loading fs/xfs/xfs_fs.h +3 −0 Original line number Original line Diff line number Diff line Loading @@ -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 | \ Loading fs/xfs/xfs_icache.c +94 −2 Original line number Original line Diff line number Diff line Loading @@ -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> Loading Loading @@ -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, Loading @@ -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 */ Loading @@ -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)) Loading @@ -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) Loading fs/xfs/xfs_icache.h +3 −0 Original line number Original line Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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, Loading Loading @@ -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 Loading
fs/xfs/xfs_dquot.h +15 −0 Original line number Original line Diff line number Diff line Loading @@ -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) Loading
fs/xfs/xfs_file.c +19 −4 Original line number Original line Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; } } Loading
fs/xfs/xfs_fs.h +3 −0 Original line number Original line Diff line number Diff line Loading @@ -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 | \ Loading
fs/xfs/xfs_icache.c +94 −2 Original line number Original line Diff line number Diff line Loading @@ -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> Loading Loading @@ -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, Loading @@ -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 */ Loading @@ -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)) Loading @@ -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) Loading
fs/xfs/xfs_icache.h +3 −0 Original line number Original line Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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, Loading Loading @@ -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