diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/libxfs/xfs_ag_resv.c | 85 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_ag_resv.h | 2 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_types.h | 1 | ||||
-rw-r--r-- | fs/xfs/scrub/fscounters.c | 3 | ||||
-rw-r--r-- | fs/xfs/xfs_fsops.c | 9 | ||||
-rw-r--r-- | fs/xfs/xfs_mount.h | 25 | ||||
-rw-r--r-- | fs/xfs/xfs_rtalloc.c | 13 | ||||
-rw-r--r-- | fs/xfs/xfs_trace.h | 31 |
8 files changed, 159 insertions, 10 deletions
diff --git a/fs/xfs/libxfs/xfs_ag_resv.c b/fs/xfs/libxfs/xfs_ag_resv.c index d12feec486fb..c0cc9c823c9e 100644 --- a/fs/xfs/libxfs/xfs_ag_resv.c +++ b/fs/xfs/libxfs/xfs_ag_resv.c @@ -21,6 +21,7 @@ #include "xfs_ialloc_btree.h" #include "xfs_sb.h" #include "xfs_ag_resv.h" +#include "xfs_ag.h" /* * Per-AG Block Reservations @@ -75,6 +76,10 @@ xfs_ag_resv_critical( xfs_extlen_t btree_maxlevels; switch (type) { + case XFS_AG_RESV_RTMETADATA: + avail = percpu_counter_sum(&pag->pag_mount->m_fdblocks); + orig = pag->pag_mount->m_rtmeta_resv.ar_asked; + break; case XFS_AG_RESV_METADATA: avail = pag->pagf_freeblks - pag->pag_rmapbt_resv.ar_reserved; orig = pag->pag_meta_resv.ar_asked; @@ -111,6 +116,7 @@ xfs_ag_resv_needed( len = pag->pag_meta_resv.ar_reserved + pag->pag_rmapbt_resv.ar_reserved; switch (type) { + case XFS_AG_RESV_RTMETADATA: case XFS_AG_RESV_METADATA: case XFS_AG_RESV_RMAPBT: len -= xfs_perag_resv(pag, type)->ar_reserved; @@ -177,6 +183,82 @@ xfs_ag_resv_free( return error; } +/* Clean out a rt reservation */ +int +xfs_rt_resv_free( + struct xfs_mount *mp) +{ + int error; + + trace_xfs_rt_resv_free(mp, 0); + + error = xfs_mod_fdblocks(mp, mp->m_rtmeta_resv.ar_reserved, true); + if (error) + return error; + + mp->m_rtmeta_resv.ar_reserved = 0; + mp->m_rtmeta_resv.ar_asked = 0; + mp->m_rtmeta_resv.ar_orig_reserved = 0; + return 0; +} + +static int +__xfs_rt_resv_init( + struct xfs_mount *mp, + xfs_filblks_t ask, + xfs_filblks_t used) +{ + xfs_filblks_t hidden_space; + int error; + + /* + * 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. + */ + if (used > ask) + ask = used; + hidden_space = ask - used; + + error = xfs_mod_fdblocks(mp, -(int64_t)hidden_space, true); + if (error) { + trace_xfs_ag_resv_init_error(mp, NULLAGNUMBER, error, + _RET_IP_); + xfs_warn(mp, +"Space reservation for rt metadata failed. Filesystem may run out of space."); + return error; + } + + mp->m_rtmeta_resv.ar_asked = ask; + mp->m_rtmeta_resv.ar_orig_reserved = hidden_space; + mp->m_rtmeta_resv.ar_reserved = ask - used; + + trace_xfs_rt_resv_init(mp, ask); + return 0; +} + +/* Create a rt metadata block reservation. */ +int +xfs_rt_resv_init( + struct xfs_mount *mp, + struct xfs_trans *tp) +{ + xfs_filblks_t ask; + xfs_filblks_t used; + int error; + + /* Create the rt metadata reservation. */ + if (mp->m_rtmeta_resv.ar_asked == 0) { + ask = used = 0; + + error = __xfs_rt_resv_init(mp, ask, used); + if (error) + return error; + } + + return 0; +} + static int __xfs_ag_resv_init( struct xfs_perag *pag, @@ -202,6 +284,7 @@ __xfs_ag_resv_init( */ hidden_space = ask; break; + case XFS_AG_RESV_RTMETADATA: case XFS_AG_RESV_METADATA: /* * Space taken by all other metadata btrees are accounted @@ -335,6 +418,7 @@ xfs_ag_resv_alloc_extent( switch (type) { case XFS_AG_RESV_AGFL: return; + case XFS_AG_RESV_RTMETADATA: case XFS_AG_RESV_METADATA: case XFS_AG_RESV_RMAPBT: resv = xfs_perag_resv(pag, type); @@ -377,6 +461,7 @@ xfs_ag_resv_free_extent( switch (type) { case XFS_AG_RESV_AGFL: return; + case XFS_AG_RESV_RTMETADATA: case XFS_AG_RESV_METADATA: case XFS_AG_RESV_RMAPBT: resv = xfs_perag_resv(pag, type); diff --git a/fs/xfs/libxfs/xfs_ag_resv.h b/fs/xfs/libxfs/xfs_ag_resv.h index d851510e01de..3899063d5892 100644 --- a/fs/xfs/libxfs/xfs_ag_resv.h +++ b/fs/xfs/libxfs/xfs_ag_resv.h @@ -8,6 +8,8 @@ int xfs_ag_resv_free(struct xfs_perag *pag); int xfs_ag_resv_init(struct xfs_perag *pag, struct xfs_trans *tp); +int xfs_rt_resv_free(struct xfs_mount *mp); +int xfs_rt_resv_init(struct xfs_mount *mp, struct xfs_trans *tp); bool xfs_ag_resv_critical(struct xfs_perag *pag, enum xfs_ag_resv_type type); xfs_filblks_t xfs_ag_resv_needed(struct xfs_perag *pag, diff --git a/fs/xfs/libxfs/xfs_types.h b/fs/xfs/libxfs/xfs_types.h index 24b859da68ef..9c7b4cbfd602 100644 --- a/fs/xfs/libxfs/xfs_types.h +++ b/fs/xfs/libxfs/xfs_types.h @@ -173,6 +173,7 @@ enum xfs_ag_resv_type { XFS_AG_RESV_AGFL, XFS_AG_RESV_METADATA, XFS_AG_RESV_RMAPBT, + XFS_AG_RESV_RTMETADATA, /* * Don't increase fdblocks when freeing extent. This is a pony for diff --git a/fs/xfs/scrub/fscounters.c b/fs/xfs/scrub/fscounters.c index 812a69cc723a..04a766924d75 100644 --- a/fs/xfs/scrub/fscounters.c +++ b/fs/xfs/scrub/fscounters.c @@ -233,6 +233,9 @@ retry: delayed = percpu_counter_sum(&mp->m_delalloc_blks); fsc->fdblocks -= delayed; + /* Account blocks reserved for rt metadata btrees. */ + fsc->fdblocks -= mp->m_rtmeta_resv.ar_reserved; + trace_xchk_fscounters_calc(mp, fsc->icount, fsc->ifree, fsc->fdblocks, delayed); diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index ef6a700679ad..9c7c104e47fa 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -535,6 +535,12 @@ xfs_fs_reserve_ag_blocks( error = err2; } + if (xfs_sb_version_hasrealtime(&mp->m_sb)) { + err2 = xfs_rt_resv_init(mp, NULL); + if (err2 && !error) + error = err2; + } + if (error && error != -ENOSPC) { xfs_warn(mp, "Error %d reserving per-AG metadata reserve pool.", error); @@ -556,6 +562,9 @@ xfs_fs_unreserve_ag_blocks( int error = 0; int err2; + if (xfs_sb_version_hasrealtime(&mp->m_sb)) + error = xfs_rt_resv_free(mp); + for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) { pag = xfs_perag_get(mp, agno); err2 = xfs_ag_resv_free(pag); diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 3a4ce7be8d6b..f6bc51a7f985 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -59,6 +59,16 @@ struct xfs_hook_chain { struct srcu_notifier_head head; }; +/* metadata object block reservation data structure */ +struct xfs_ag_resv { + /* number of blocks originally reserved here */ + xfs_filblks_t ar_orig_reserved; + /* number of blocks reserved here */ + xfs_filblks_t ar_reserved; + /* number of blocks originally asked for */ + xfs_filblks_t ar_asked; +}; + /* * The struct xfsmount layout is optimised to separate read-mostly variables * from variables that are frequently modified. We put the read-mostly variables @@ -233,6 +243,9 @@ typedef struct xfs_mount { * while a repair freeze is in progress. */ struct mutex m_scrub_freeze; + + /* Blocks reserved for all kinds of inode-based (rt) metadata. */ + struct xfs_ag_resv m_rtmeta_resv; } xfs_mount_t; #define M_IGEO(mp) (&(mp)->m_ino_geo) @@ -319,16 +332,6 @@ xfs_daddr_to_agbno(struct xfs_mount *mp, xfs_daddr_t d) return (xfs_agblock_t) do_div(ld, mp->m_sb.sb_agblocks); } -/* per-AG block reservation data structures*/ -struct xfs_ag_resv { - /* number of blocks originally reserved here */ - xfs_filblks_t ar_orig_reserved; - /* number of blocks reserved here */ - xfs_filblks_t ar_reserved; - /* number of blocks originally asked for */ - xfs_filblks_t ar_asked; -}; - /* * Per-ag incore structure, copies of information in agf and agi, to improve the * performance of allocation group selection. @@ -420,6 +423,8 @@ xfs_perag_resv( return &pag->pag_meta_resv; case XFS_AG_RESV_RMAPBT: return &pag->pag_rmapbt_resv; + case XFS_AG_RESV_RTMETADATA: + return &pag->pag_mount->m_rtmeta_resv; default: return NULL; } diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index 52d243188569..ed98a7d412c2 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -22,6 +22,9 @@ #include "xfs_health.h" #include "xfs_da_format.h" #include "xfs_imeta.h" +#include "xfs_sb.h" +#include "xfs_alloc.h" +#include "xfs_ag_resv.h" /* * Read and return the summary information for a given extent size, @@ -1110,6 +1113,16 @@ error_cancel: /* Update secondary superblocks now the physical grow has completed */ error = xfs_update_secondary_sbs(mp); + if (error) + goto out_free; + + /* Reset the rt metadata btree space reservations. */ + error = xfs_rt_resv_free(mp); + if (error) + goto out_free; + error = xfs_rt_resv_init(mp, NULL); + if (error == -ENOSPC) + error = 0; out_free: /* diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 6efd54a618e0..970d688c30a8 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -2773,6 +2773,37 @@ DEFINE_AG_RESV_EVENT(xfs_ag_resv_needed); DEFINE_AG_ERROR_EVENT(xfs_ag_resv_free_error); DEFINE_AG_ERROR_EVENT(xfs_ag_resv_init_error); +DECLARE_EVENT_CLASS(xfs_rt_resv_class, + TP_PROTO(struct xfs_mount *mp, xfs_filblks_t len), + TP_ARGS(mp, len), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned long long, freeblks) + __field(unsigned long long, reserved) + __field(unsigned long long, asked) + __field(unsigned long long, len) + ), + TP_fast_assign( + __entry->dev = mp->m_super->s_dev; + __entry->freeblks = percpu_counter_sum(&mp->m_fdblocks); + __entry->reserved = mp->m_rtmeta_resv.ar_reserved; + __entry->asked = mp->m_rtmeta_resv.ar_asked; + __entry->len = len; + ), + TP_printk("dev %d:%d freeblks %llu resv %llu ask %llu len %llu", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->freeblks, + __entry->reserved, + __entry->asked, + __entry->len) +) +#define DEFINE_RT_RESV_EVENT(name) \ +DEFINE_EVENT(xfs_rt_resv_class, name, \ + TP_PROTO(struct xfs_mount *mp, xfs_filblks_t len), \ + TP_ARGS(mp, len)) +DEFINE_RT_RESV_EVENT(xfs_rt_resv_init); +DEFINE_RT_RESV_EVENT(xfs_rt_resv_free); + /* refcount tracepoint classes */ /* reuse the discard trace class for agbno/aglen-based traces */ |