This patch dumps buffer cache size per mount. Counters for buffer size 1 BB to 257 BBs are stored in struct xfs_mount. The last one is for size >= 257. When dumping the cache size, we try to dump for each exact buffer size if the output memory buffer (PAGE SIZE) is enough. Buffer_size Count Total_size size1 count1 size2 count2 .... >=size_last count_size_last total_size_in_BBs For each size, we 1 dump it the count is not zero 2 dump two fields: size of the buffers and the count of those buffers so that the total size can be calculated by size * count. We don't dump the total size to save space. 3 for the last size (if count is not zero) we dump three fields: the buffer size prefixed by ">=", count of those buffers and the total size. If the space is enough to store about exact output, we are good. In case the PAGE SIZE is not enough for above format, we then change to the following size range based format: Buffer_size Count Total_size <=1 count total /* for buffers with size 1 to 1 */ <=2 count total /* for buffers with size 2 to 2 */ <=4 count total /* for buffers with size 3 to 4 */ .... <=256 count total /* for buffers with size 129 to 256 */ >=257 count total /* for buffers with size >= 257 */ For each line, we 1 dump three fields: size range, count of those buffers and total size of them 2 dump it no matter count if zero or not Signed-off-by: Wengang Wang <wen.gang.wang@xxxxxxxxxx> --- fs/xfs/xfs_buf.c | 36 +++++++++++++- fs/xfs/xfs_mount.h | 20 ++++++++ fs/xfs/xfs_super.c | 36 ++++++++++++-- fs/xfs/xfs_sysfs.c | 118 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 203 insertions(+), 7 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 1a2b3f06fa71..9b0aae04745b 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -99,6 +99,28 @@ xfs_buf_free_callback( kmem_cache_free(xfs_buf_cache, bp); } +static void xfs_cache_count_inc(struct xfs_mount *mp, int bbs) +{ + int idx = bbs - 1; + + if (idx > XFS_CACHECOUNT_SIZE_MAX_IDX) { + idx = XFS_CACHECOUNT_SIZE_MAX_IDX; + percpu_counter_add(&mp->m_cache_cts.last_total_bbs, bbs); + } + percpu_counter_inc(&mp->m_cache_cts.counters[idx]); +} + +static void xfs_cache_count_dec(struct xfs_mount *mp, int bbs) +{ + int idx = bbs - 1; + + if (idx > XFS_CACHECOUNT_SIZE_MAX_IDX) { + idx = XFS_CACHECOUNT_SIZE_MAX_IDX; + percpu_counter_sub(&mp->m_cache_cts.last_total_bbs, bbs); + } + percpu_counter_dec(&mp->m_cache_cts.counters[idx]); +} + static void xfs_buf_free( struct xfs_buf *bp) @@ -113,6 +135,8 @@ xfs_buf_free( if (!xfs_buftarg_is_mem(bp->b_target) && size >= PAGE_SIZE) mm_account_reclaimed_pages(howmany(size, PAGE_SHIFT)); + xfs_cache_count_dec(bp->b_mount, bp->b_length); + if (is_vmalloc_addr(bp->b_addr)) vfree(bp->b_addr); else if (bp->b_flags & _XBF_KMEM) @@ -196,8 +220,14 @@ xfs_buf_alloc_backing_mem( * allocation for all power of two sizes, which matches most of the * smaller than PAGE_SIZE buffers used by XFS. */ - if (size < PAGE_SIZE && is_power_of_2(size)) - return xfs_buf_alloc_kmem(bp, size, gfp_mask); + if (size < PAGE_SIZE && is_power_of_2(size)) { + int ret; + + ret = xfs_buf_alloc_kmem(bp, size, gfp_mask); + if (ret == 0) + xfs_cache_count_inc(bp->b_mount, bp->b_length); + return ret; + } /* * Don't bother with the retry loop for single PAGE allocations: vmalloc @@ -231,6 +261,7 @@ xfs_buf_alloc_backing_mem( goto fallback; } bp->b_addr = folio_address(folio); + xfs_cache_count_inc(bp->b_mount, bp->b_length); trace_xfs_buf_backing_folio(bp, _RET_IP_); return 0; @@ -245,6 +276,7 @@ xfs_buf_alloc_backing_mem( memalloc_retry_wait(gfp_mask); } + xfs_cache_count_inc(bp->b_mount, bp->b_length); trace_xfs_buf_backing_vmalloc(bp, _RET_IP_); return 0; } diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index e5192c12e7ac..9141d0122fc9 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -135,6 +135,21 @@ struct xfs_freecounter { uint64_t res_saved; }; +#define XFS_CACHECOUNT_SIZE_MAX_IDX 256 +struct xfs_cachecounters { + /* + * index to the array is the size of buffers in BBs minus 1. + * elements are the numbers of buffers for each size. + * last element is for exact size and biger sizes + */ + struct percpu_counter counters[XFS_CACHECOUNT_SIZE_MAX_IDX + 1]; + + /* total number of BBs for last element */ + struct percpu_counter last_total_bbs; + + struct xfs_kobj kobj; +}; + /* * The struct xfsmount layout is optimised to separate read-mostly variables * from variables that are frequently modified. We put the read-mostly variables @@ -276,6 +291,11 @@ typedef struct xfs_mount { */ atomic64_t m_allocbt_blks; + /* + * Count number of buffers and totol size of them per size. + */ + struct xfs_cachecounters m_cache_cts; + struct xfs_groups m_groups[XG_TYPE_MAX]; struct delayed_work m_reclaim_work; /* background inode reclaim */ struct xfs_zone_info *m_zone_info; /* zone allocator information */ diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 3be041647ec1..30bdb9382786 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -1121,7 +1121,7 @@ xfs_init_percpu_counters( struct xfs_mount *mp) { int error; - int i; + int i, j; error = percpu_counter_init(&mp->m_icount, 0, GFP_KERNEL); if (error) @@ -1142,15 +1142,38 @@ xfs_init_percpu_counters( for (i = 0; i < XC_FREE_NR; i++) { error = percpu_counter_init(&mp->m_free[i].count, 0, GFP_KERNEL); - if (error) + if (error) { + j = i; goto free_freecounters; + } + } + + error = percpu_counter_init(&mp->m_cache_cts.last_total_bbs, + 0, GFP_KERNEL); + if (error) { + j = XC_FREE_NR; + goto free_freecounters; + } + + for (i = 0; i <= XFS_CACHECOUNT_SIZE_MAX_IDX; i++) { + error = percpu_counter_init(&mp->m_cache_cts.counters[i], + 0, GFP_KERNEL); + if (error) { + j = i; + goto free_cache_cts; + } } return 0; +free_cache_cts: + percpu_counter_destroy(&mp->m_cache_cts.last_total_bbs); + while (--j >= 0) + percpu_counter_destroy(&mp->m_cache_cts.counters[j]); + j = XC_FREE_NR; free_freecounters: - while (--i >= 0) - percpu_counter_destroy(&mp->m_free[i].count); + while (--j >= 0) + percpu_counter_destroy(&mp->m_free[j].count); percpu_counter_destroy(&mp->m_delalloc_rtextents); free_delalloc: percpu_counter_destroy(&mp->m_delalloc_blks); @@ -1177,8 +1200,11 @@ static void xfs_destroy_percpu_counters( struct xfs_mount *mp) { - enum xfs_free_counter i; + int i; + percpu_counter_destroy(&mp->m_cache_cts.last_total_bbs); + for (i = 0; i <= XFS_CACHECOUNT_SIZE_MAX_IDX; i++) + percpu_counter_destroy(&mp->m_cache_cts.counters[i]); for (i = 0; i < XC_FREE_NR; i++) percpu_counter_destroy(&mp->m_free[i].count); percpu_counter_destroy(&mp->m_icount); diff --git a/fs/xfs/xfs_sysfs.c b/fs/xfs/xfs_sysfs.c index 7a5c5ef2db92..3c2f948c3165 100644 --- a/fs/xfs/xfs_sysfs.c +++ b/fs/xfs/xfs_sysfs.c @@ -467,6 +467,110 @@ const struct kobj_type xfs_log_ktype = { .default_groups = xfs_log_groups, }; +static const struct kobj_type xfs_cache_ktype = { + .release = xfs_sysfs_release, + .sysfs_ops = &xfs_sysfs_ops, +}; + +/* .../<dev>/cache/size */ +STATIC ssize_t +size_show(struct kobject *kobject, char *buf) +{ + int left_size = PAGE_SIZE, offset, len, i, power_size; + s64 power_nr, power_total, c, last; + struct xfs_cachecounters *cts; + struct xfs_kobj *xobj; + struct xfs_mount *mp; + + xobj = to_kobj(kobject); + cts = container_of(xobj, struct xfs_cachecounters, kobj); + mp = container_of(cts, struct xfs_mount, m_cache_cts); + + /* try to dump for each exact size if buf is big enough */ + len = snprintf(buf, PAGE_SIZE, "Buffer_size Count Total_size\n"); + left_size = PAGE_SIZE - len; + offset = len; + + /* + * for elements except for the last, print size (BBs) and count only. + * as total can get be get as size * count. + */ + for (i = 0; i < XFS_CACHECOUNT_SIZE_MAX_IDX; i++) { + c = percpu_counter_sum_positive(&mp->m_cache_cts.counters[i]); + /* don't print on zeros to save buffer space */ + if (c == 0) + continue; + + /* "i + 1" is buf size */ + len = snprintf(buf + offset, left_size, "%d %lld\n", i+1, c); + left_size -= len; + offset += len; + if (left_size == 0) + goto power_histogram; + } + + /* last element, print size, count and total size */ + c = percpu_counter_sum_positive(&mp->m_cache_cts.counters[i]); + if (c) { + last = percpu_counter_sum_positive( + &mp->m_cache_cts.last_total_bbs); + + /* "i + 1" is buf size */ + len = snprintf(buf + offset, left_size, ">=%d %lld %lld\n", + i + 1, c, last); + left_size -= len; + if (left_size == 0) + goto power_histogram; + } + return PAGE_SIZE - left_size; + +power_histogram: + len = snprintf(buf, PAGE_SIZE, "Buffer_size Count Total_size\n"); + left_size = PAGE_SIZE - len; + offset = len; + + power_size = 1; + power_total = 0; + power_nr = 0; + for (i = 0; i < XFS_CACHECOUNT_SIZE_MAX_IDX; i++) { + int buf_size; + + buf_size = i + 1; + if (buf_size > power_size) { + len = snprintf(buf + offset, left_size, + "<=%d %lld %lld\n", + power_size, power_nr, power_total); + left_size -= len; + offset += len; + power_size <<= 1; + power_total = 0; + power_nr = 0; + } + c = percpu_counter_sum_positive(&mp->m_cache_cts.counters[i]); + if (c == 0) + continue; + + power_nr += c; + power_total += c * buf_size; + } + + len = snprintf(buf + offset, left_size, "<=%d %lld %lld\n", + i, power_nr, power_total); + left_size -= len; + offset += len; + + /* the last element contains count/total for bigger sizes too */ + c = percpu_counter_sum_positive(&mp->m_cache_cts.counters[i]); + power_total = percpu_counter_sum_positive( + &mp->m_cache_cts.last_total_bbs); + len = snprintf(buf + offset, left_size, ">=%d %lld %lld\n", + i + 1, power_nr, power_total); + left_size -= len; + + return PAGE_SIZE - left_size; +} +XFS_SYSFS_ATTR_RO(size); + /* * Metadata IO error configuration * @@ -810,8 +914,21 @@ xfs_mount_sysfs_init( goto out_remove_error_dir; } + /* .../xfs/<dev>/cache/ */ + error = xfs_sysfs_init(&mp->m_cache_cts.kobj, &xfs_cache_ktype, + &mp->m_kobj, "cache"); + if (error) + goto out_remove_error_dir; + + error = sysfs_create_file(&mp->m_cache_cts.kobj.kobject, + ATTR_LIST(size)); + if (error) + goto out_remove_cache_dir; + return 0; +out_remove_cache_dir: + xfs_sysfs_del(&mp->m_cache_cts.kobj); out_remove_error_dir: xfs_sysfs_del(&mp->m_error_kobj); out_remove_stats_dir: @@ -841,6 +958,7 @@ xfs_mount_sysfs_del( xfs_sysfs_del(&mp->m_error_meta_kobj); xfs_sysfs_del(&mp->m_error_kobj); xfs_sysfs_del(&mp->m_stats.xs_kobj); + xfs_sysfs_del(&mp->m_cache_cts.kobj); xfs_sysfs_del(&mp->m_kobj); } -- 2.39.5 (Apple Git-154)