diff options
Diffstat (limited to 'fs/xfs/libxfs')
-rw-r--r-- | fs/xfs/libxfs/xfs_ag_resv.c | 3 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_errortag.h | 4 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_imeta.c | 187 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_imeta.h | 11 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_types.h | 7 |
5 files changed, 211 insertions, 1 deletions
diff --git a/fs/xfs/libxfs/xfs_ag_resv.c b/fs/xfs/libxfs/xfs_ag_resv.c index 8723cd0d3f58..75c04319e9e3 100644 --- a/fs/xfs/libxfs/xfs_ag_resv.c +++ b/fs/xfs/libxfs/xfs_ag_resv.c @@ -113,6 +113,7 @@ xfs_ag_resv_needed( case XFS_AG_RESV_RMAPBT: len -= xfs_perag_resv(pag, type)->ar_reserved; break; + case XFS_AG_RESV_IMETA: case XFS_AG_RESV_NONE: /* empty */ break; @@ -347,6 +348,7 @@ xfs_ag_resv_alloc_extent( switch (type) { case XFS_AG_RESV_AGFL: + case XFS_AG_RESV_IMETA: return; case XFS_AG_RESV_METADATA: case XFS_AG_RESV_RMAPBT: @@ -389,6 +391,7 @@ xfs_ag_resv_free_extent( switch (type) { case XFS_AG_RESV_AGFL: + case XFS_AG_RESV_IMETA: return; case XFS_AG_RESV_METADATA: case XFS_AG_RESV_RMAPBT: diff --git a/fs/xfs/libxfs/xfs_errortag.h b/fs/xfs/libxfs/xfs_errortag.h index d26620337036..30c417e7736a 100644 --- a/fs/xfs/libxfs/xfs_errortag.h +++ b/fs/xfs/libxfs/xfs_errortag.h @@ -63,7 +63,8 @@ #define XFS_ERRTAG_DA_LEAF_SPLIT 40 #define XFS_ERRTAG_ATTR_LEAF_TO_NODE 41 #define XFS_ERRTAG_SWAPEXT_FINISH_ONE 42 -#define XFS_ERRTAG_MAX 43 +#define XFS_ERRTAG_IMETA_RESV_CRITICAL 43 +#define XFS_ERRTAG_MAX 44 /* * Random factors for above tags, 1 means always, 2 means 1/2 time, etc. @@ -111,5 +112,6 @@ #define XFS_RANDOM_DA_LEAF_SPLIT 1 #define XFS_RANDOM_ATTR_LEAF_TO_NODE 1 #define XFS_RANDOM_SWAPEXT_FINISH_ONE 1 +#define XFS_RANDOM_IMETA_RESV_CRITICAL 4 #endif /* __XFS_ERRORTAG_H_ */ diff --git a/fs/xfs/libxfs/xfs_imeta.c b/fs/xfs/libxfs/xfs_imeta.c index dbf08b25f26a..f996f5840246 100644 --- a/fs/xfs/libxfs/xfs_imeta.c +++ b/fs/xfs/libxfs/xfs_imeta.c @@ -27,6 +27,10 @@ #include "xfs_dir2_priv.h" #include "xfs_ag.h" #include "xfs_health.h" +#include "xfs_errortag.h" +#include "xfs_error.h" +#include "xfs_btree.h" +#include "xfs_alloc.h" /* * Metadata Inode Number Management @@ -1208,3 +1212,186 @@ xfs_imeta_free_path( kfree(path->im_path); kfree(path); } + +/* + * Is the amount of space that could be allocated towards a given metadata + * file at or beneath a certain threshold? + */ +static inline bool +xfs_imeta_resv_can_cover( + struct xfs_inode *ip, + int64_t rhs) +{ + /* + * The amount of space that can be allocated to this metadata file is + * the remaining reservation for the particular metadata file + the + * global free block count. Take care of the first case to avoid + * touching the per-cpu counter. + */ + if (ip->i_delayed_blks >= rhs) + return true; + + /* + * There aren't enough blocks left in the inode's reservation, but it + * isn't critical unless there also isn't enough free space. + */ + return __percpu_counter_compare(&ip->i_mount->m_fdblocks, + rhs - ip->i_delayed_blks, 2048) >= 0; +} + +/* + * Is this metadata file critically low on blocks? For now we'll define that + * as the number of blocks we can get our hands on being less than 10% of what + * we reserved or less than some arbitrary number (maximum btree height). + */ +bool +xfs_imeta_resv_critical( + struct xfs_inode *ip) +{ + uint64_t asked_low_water; + + if (!ip) + return false; + + ASSERT(xfs_is_metadata_inode(ip)); + trace_xfs_imeta_resv_critical(ip, 0); + + if (!xfs_imeta_resv_can_cover(ip, ip->i_mount->m_rtbtree_maxlevels)) + return true; + + asked_low_water = div_u64(ip->i_meta_resv_asked, 10); + if (!xfs_imeta_resv_can_cover(ip, asked_low_water)) + return true; + + return XFS_TEST_ERROR(false, ip->i_mount, + XFS_ERRTAG_IMETA_RESV_CRITICAL); +} + +/* Allocate a block from the metadata file's reservation. */ +void +xfs_imeta_resv_alloc_extent( + struct xfs_inode *ip, + struct xfs_alloc_arg *args) +{ + int64_t len = args->len; + + ASSERT(xfs_is_metadata_inode(ip)); + ASSERT(args->resv == XFS_AG_RESV_IMETA); + + trace_xfs_imeta_resv_alloc_extent(ip, args->len); + + /* + * Allocate the blocks from the metadata inode's block reservation + * and update the ondisk sb counter. + */ + if (ip->i_delayed_blks > 0) { + int64_t from_resv; + + from_resv = min_t(int64_t, len, ip->i_delayed_blks); + ip->i_delayed_blks -= from_resv; + xfs_mod_delalloc(ip->i_mount, -from_resv); + xfs_trans_mod_sb(args->tp, XFS_TRANS_SB_RES_FDBLOCKS, + -from_resv); + len -= from_resv; + } + + /* + * Any allocation in excess of the reservation requires in-core and + * on-disk fdblocks updates. + */ + if (len) + xfs_trans_mod_sb(args->tp, XFS_TRANS_SB_FDBLOCKS, -len); + + ip->i_nblocks += args->len; +} + +/* Free a block to the metadata file's reservation. */ +void +xfs_imeta_resv_free_extent( + struct xfs_inode *ip, + struct xfs_trans *tp, + xfs_filblks_t len) +{ + int64_t to_resv; + + ASSERT(xfs_is_metadata_inode(ip)); + trace_xfs_imeta_resv_free_extent(ip, len); + + ip->i_nblocks -= len; + + /* + * Add the freed blocks back into the inode's delalloc reservation + * until it reaches the maximum size. Update the ondisk fdblocks only. + */ + to_resv = ip->i_meta_resv_asked - (ip->i_nblocks + ip->i_delayed_blks); + if (to_resv > 0) { + to_resv = min_t(int64_t, to_resv, len); + ip->i_delayed_blks += to_resv; + xfs_mod_delalloc(ip->i_mount, to_resv); + xfs_trans_mod_sb(tp, XFS_TRANS_SB_RES_FDBLOCKS, to_resv); + len -= to_resv; + } + + /* + * Everything else goes back to the filesystem, so update the in-core + * and on-disk counters. + */ + if (len) + xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, len); +} + +/* Release a metadata file's space reservation. */ +void +xfs_imeta_resv_free_inode( + struct xfs_inode *ip) +{ + if (!ip) + return; + + ASSERT(xfs_is_metadata_inode(ip)); + trace_xfs_imeta_resv_free(ip, 0); + + xfs_mod_delalloc(ip->i_mount, -ip->i_delayed_blks); + xfs_mod_fdblocks(ip->i_mount, ip->i_delayed_blks, true); + ip->i_delayed_blks = 0; + ip->i_meta_resv_asked = 0; +} + +/* Set up a metadata file's space reservation. */ +int +xfs_imeta_resv_init_inode( + struct xfs_inode *ip, + xfs_filblks_t ask) +{ + xfs_filblks_t hidden_space; + xfs_filblks_t used; + int error; + + if (!ip || ip->i_meta_resv_asked > 0) + return 0; + + ASSERT(xfs_is_metadata_inode(ip)); + + /* + * Space taken by all other metadata btrees are accounted on-disk as + * used space. We therefore only hide the space that is reserved but + * not used by the trees. + */ + used = ip->i_nblocks; + if (used > ask) + ask = used; + hidden_space = ask - used; + + error = xfs_mod_fdblocks(ip->i_mount, -(int64_t)hidden_space, true); + if (error) { + trace_xfs_imeta_resv_init_error(ip, error, _RET_IP_); + return error; + } + + xfs_mod_delalloc(ip->i_mount, hidden_space); + ip->i_delayed_blks = hidden_space; + ip->i_meta_resv_asked = ask; + + trace_xfs_imeta_resv_init(ip, ask); + return 0; +} diff --git a/fs/xfs/libxfs/xfs_imeta.h b/fs/xfs/libxfs/xfs_imeta.h index 7840087b71da..c3137be4c47c 100644 --- a/fs/xfs/libxfs/xfs_imeta.h +++ b/fs/xfs/libxfs/xfs_imeta.h @@ -84,6 +84,17 @@ void xfs_imeta_droplink(struct xfs_inode *ip); unsigned int xfs_imeta_create_space_res(struct xfs_mount *mp); unsigned int xfs_imeta_unlink_space_res(struct xfs_mount *mp); +/* Space reservations for metadata inodes. */ +struct xfs_alloc_arg; + +bool xfs_imeta_resv_critical(struct xfs_inode *ip); +void xfs_imeta_resv_alloc_extent(struct xfs_inode *ip, + struct xfs_alloc_arg *args); +void xfs_imeta_resv_free_extent(struct xfs_inode *ip, struct xfs_trans *tp, + xfs_filblks_t len); +void xfs_imeta_resv_free_inode(struct xfs_inode *ip); +int xfs_imeta_resv_init_inode(struct xfs_inode *ip, xfs_filblks_t ask); + /* Must be implemented by the libxfs client */ int xfs_imeta_iget(struct xfs_mount *mp, xfs_ino_t ino, unsigned char ftype, struct xfs_inode **ipp); diff --git a/fs/xfs/libxfs/xfs_types.h b/fs/xfs/libxfs/xfs_types.h index 99f753227ee9..5479822b77fc 100644 --- a/fs/xfs/libxfs/xfs_types.h +++ b/fs/xfs/libxfs/xfs_types.h @@ -219,6 +219,13 @@ enum xfs_ag_resv_type { * altering fdblocks. If you think you need this you're wrong. */ XFS_AG_RESV_IGNORE, + + /* + * This allocation activity is being done on behalf of a metadata file. + * These files maintain their own permanent space reservations and are + * required to adjust fdblocks using the xfs_imeta_resv_* helpers. + */ + XFS_AG_RESV_IMETA, }; /* Results of scanning a btree keyspace to check occupancy. */ |