162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * f2fs iostat support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2021 Google LLC 662306a36Sopenharmony_ci * Author: Daeho Jeong <daehojeong@google.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/f2fs_fs.h> 1162306a36Sopenharmony_ci#include <linux/seq_file.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "f2fs.h" 1462306a36Sopenharmony_ci#include "iostat.h" 1562306a36Sopenharmony_ci#include <trace/events/f2fs.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic struct kmem_cache *bio_iostat_ctx_cache; 1862306a36Sopenharmony_cistatic mempool_t *bio_iostat_ctx_pool; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic inline unsigned long long iostat_get_avg_bytes(struct f2fs_sb_info *sbi, 2162306a36Sopenharmony_ci enum iostat_type type) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci return sbi->iostat_count[type] ? div64_u64(sbi->iostat_bytes[type], 2462306a36Sopenharmony_ci sbi->iostat_count[type]) : 0; 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define IOSTAT_INFO_SHOW(name, type) \ 2862306a36Sopenharmony_ci seq_printf(seq, "%-23s %-16llu %-16llu %-16llu\n", \ 2962306a36Sopenharmony_ci name":", sbi->iostat_bytes[type], \ 3062306a36Sopenharmony_ci sbi->iostat_count[type], \ 3162306a36Sopenharmony_ci iostat_get_avg_bytes(sbi, type)) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciint __maybe_unused iostat_info_seq_show(struct seq_file *seq, void *offset) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct super_block *sb = seq->private; 3662306a36Sopenharmony_ci struct f2fs_sb_info *sbi = F2FS_SB(sb); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (!sbi->iostat_enable) 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci seq_printf(seq, "time: %-16llu\n", ktime_get_real_seconds()); 4262306a36Sopenharmony_ci seq_printf(seq, "\t\t\t%-16s %-16s %-16s\n", 4362306a36Sopenharmony_ci "io_bytes", "count", "avg_bytes"); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* print app write IOs */ 4662306a36Sopenharmony_ci seq_puts(seq, "[WRITE]\n"); 4762306a36Sopenharmony_ci IOSTAT_INFO_SHOW("app buffered data", APP_BUFFERED_IO); 4862306a36Sopenharmony_ci IOSTAT_INFO_SHOW("app direct data", APP_DIRECT_IO); 4962306a36Sopenharmony_ci IOSTAT_INFO_SHOW("app mapped data", APP_MAPPED_IO); 5062306a36Sopenharmony_ci IOSTAT_INFO_SHOW("app buffered cdata", APP_BUFFERED_CDATA_IO); 5162306a36Sopenharmony_ci IOSTAT_INFO_SHOW("app mapped cdata", APP_MAPPED_CDATA_IO); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* print fs write IOs */ 5462306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs data", FS_DATA_IO); 5562306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs cdata", FS_CDATA_IO); 5662306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs node", FS_NODE_IO); 5762306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs meta", FS_META_IO); 5862306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs gc data", FS_GC_DATA_IO); 5962306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs gc node", FS_GC_NODE_IO); 6062306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs cp data", FS_CP_DATA_IO); 6162306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs cp node", FS_CP_NODE_IO); 6262306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs cp meta", FS_CP_META_IO); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* print app read IOs */ 6562306a36Sopenharmony_ci seq_puts(seq, "[READ]\n"); 6662306a36Sopenharmony_ci IOSTAT_INFO_SHOW("app buffered data", APP_BUFFERED_READ_IO); 6762306a36Sopenharmony_ci IOSTAT_INFO_SHOW("app direct data", APP_DIRECT_READ_IO); 6862306a36Sopenharmony_ci IOSTAT_INFO_SHOW("app mapped data", APP_MAPPED_READ_IO); 6962306a36Sopenharmony_ci IOSTAT_INFO_SHOW("app buffered cdata", APP_BUFFERED_CDATA_READ_IO); 7062306a36Sopenharmony_ci IOSTAT_INFO_SHOW("app mapped cdata", APP_MAPPED_CDATA_READ_IO); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* print fs read IOs */ 7362306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs data", FS_DATA_READ_IO); 7462306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs gc data", FS_GDATA_READ_IO); 7562306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs cdata", FS_CDATA_READ_IO); 7662306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs node", FS_NODE_READ_IO); 7762306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs meta", FS_META_READ_IO); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* print other IOs */ 8062306a36Sopenharmony_ci seq_puts(seq, "[OTHER]\n"); 8162306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs discard", FS_DISCARD_IO); 8262306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs flush", FS_FLUSH_IO); 8362306a36Sopenharmony_ci IOSTAT_INFO_SHOW("fs zone reset", FS_ZONE_RESET_IO); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic inline void __record_iostat_latency(struct f2fs_sb_info *sbi) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci int io, idx; 9162306a36Sopenharmony_ci struct f2fs_iostat_latency iostat_lat[MAX_IO_TYPE][NR_PAGE_TYPE]; 9262306a36Sopenharmony_ci struct iostat_lat_info *io_lat = sbi->iostat_io_lat; 9362306a36Sopenharmony_ci unsigned long flags; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci spin_lock_irqsave(&sbi->iostat_lat_lock, flags); 9662306a36Sopenharmony_ci for (idx = 0; idx < MAX_IO_TYPE; idx++) { 9762306a36Sopenharmony_ci for (io = 0; io < NR_PAGE_TYPE; io++) { 9862306a36Sopenharmony_ci iostat_lat[idx][io].peak_lat = 9962306a36Sopenharmony_ci jiffies_to_msecs(io_lat->peak_lat[idx][io]); 10062306a36Sopenharmony_ci iostat_lat[idx][io].cnt = io_lat->bio_cnt[idx][io]; 10162306a36Sopenharmony_ci iostat_lat[idx][io].avg_lat = iostat_lat[idx][io].cnt ? 10262306a36Sopenharmony_ci jiffies_to_msecs(io_lat->sum_lat[idx][io]) / iostat_lat[idx][io].cnt : 0; 10362306a36Sopenharmony_ci io_lat->sum_lat[idx][io] = 0; 10462306a36Sopenharmony_ci io_lat->peak_lat[idx][io] = 0; 10562306a36Sopenharmony_ci io_lat->bio_cnt[idx][io] = 0; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci spin_unlock_irqrestore(&sbi->iostat_lat_lock, flags); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci trace_f2fs_iostat_latency(sbi, iostat_lat); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic inline void f2fs_record_iostat(struct f2fs_sb_info *sbi) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci unsigned long long iostat_diff[NR_IO_TYPE]; 11662306a36Sopenharmony_ci int i; 11762306a36Sopenharmony_ci unsigned long flags; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (time_is_after_jiffies(sbi->iostat_next_period)) 12062306a36Sopenharmony_ci return; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* Need double check under the lock */ 12362306a36Sopenharmony_ci spin_lock_irqsave(&sbi->iostat_lock, flags); 12462306a36Sopenharmony_ci if (time_is_after_jiffies(sbi->iostat_next_period)) { 12562306a36Sopenharmony_ci spin_unlock_irqrestore(&sbi->iostat_lock, flags); 12662306a36Sopenharmony_ci return; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci sbi->iostat_next_period = jiffies + 12962306a36Sopenharmony_ci msecs_to_jiffies(sbi->iostat_period_ms); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci for (i = 0; i < NR_IO_TYPE; i++) { 13262306a36Sopenharmony_ci iostat_diff[i] = sbi->iostat_bytes[i] - 13362306a36Sopenharmony_ci sbi->prev_iostat_bytes[i]; 13462306a36Sopenharmony_ci sbi->prev_iostat_bytes[i] = sbi->iostat_bytes[i]; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci spin_unlock_irqrestore(&sbi->iostat_lock, flags); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci trace_f2fs_iostat(sbi, iostat_diff); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci __record_iostat_latency(sbi); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_civoid f2fs_reset_iostat(struct f2fs_sb_info *sbi) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct iostat_lat_info *io_lat = sbi->iostat_io_lat; 14662306a36Sopenharmony_ci int i; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci spin_lock_irq(&sbi->iostat_lock); 14962306a36Sopenharmony_ci for (i = 0; i < NR_IO_TYPE; i++) { 15062306a36Sopenharmony_ci sbi->iostat_count[i] = 0; 15162306a36Sopenharmony_ci sbi->iostat_bytes[i] = 0; 15262306a36Sopenharmony_ci sbi->prev_iostat_bytes[i] = 0; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci spin_unlock_irq(&sbi->iostat_lock); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci spin_lock_irq(&sbi->iostat_lat_lock); 15762306a36Sopenharmony_ci memset(io_lat, 0, sizeof(struct iostat_lat_info)); 15862306a36Sopenharmony_ci spin_unlock_irq(&sbi->iostat_lat_lock); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic inline void __f2fs_update_iostat(struct f2fs_sb_info *sbi, 16262306a36Sopenharmony_ci enum iostat_type type, unsigned long long io_bytes) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci sbi->iostat_bytes[type] += io_bytes; 16562306a36Sopenharmony_ci sbi->iostat_count[type]++; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_civoid f2fs_update_iostat(struct f2fs_sb_info *sbi, struct inode *inode, 16962306a36Sopenharmony_ci enum iostat_type type, unsigned long long io_bytes) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci unsigned long flags; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (!sbi->iostat_enable) 17462306a36Sopenharmony_ci return; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci spin_lock_irqsave(&sbi->iostat_lock, flags); 17762306a36Sopenharmony_ci __f2fs_update_iostat(sbi, type, io_bytes); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (type == APP_BUFFERED_IO || type == APP_DIRECT_IO) 18062306a36Sopenharmony_ci __f2fs_update_iostat(sbi, APP_WRITE_IO, io_bytes); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (type == APP_BUFFERED_READ_IO || type == APP_DIRECT_READ_IO) 18362306a36Sopenharmony_ci __f2fs_update_iostat(sbi, APP_READ_IO, io_bytes); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci#ifdef CONFIG_F2FS_FS_COMPRESSION 18662306a36Sopenharmony_ci if (inode && f2fs_compressed_file(inode)) { 18762306a36Sopenharmony_ci if (type == APP_BUFFERED_IO) 18862306a36Sopenharmony_ci __f2fs_update_iostat(sbi, APP_BUFFERED_CDATA_IO, io_bytes); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (type == APP_BUFFERED_READ_IO) 19162306a36Sopenharmony_ci __f2fs_update_iostat(sbi, APP_BUFFERED_CDATA_READ_IO, io_bytes); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (type == APP_MAPPED_READ_IO) 19462306a36Sopenharmony_ci __f2fs_update_iostat(sbi, APP_MAPPED_CDATA_READ_IO, io_bytes); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (type == APP_MAPPED_IO) 19762306a36Sopenharmony_ci __f2fs_update_iostat(sbi, APP_MAPPED_CDATA_IO, io_bytes); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (type == FS_DATA_READ_IO) 20062306a36Sopenharmony_ci __f2fs_update_iostat(sbi, FS_CDATA_READ_IO, io_bytes); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (type == FS_DATA_IO) 20362306a36Sopenharmony_ci __f2fs_update_iostat(sbi, FS_CDATA_IO, io_bytes); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci#endif 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci spin_unlock_irqrestore(&sbi->iostat_lock, flags); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci f2fs_record_iostat(sbi); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic inline void __update_iostat_latency(struct bio_iostat_ctx *iostat_ctx, 21362306a36Sopenharmony_ci enum iostat_lat_type lat_type) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci unsigned long ts_diff; 21662306a36Sopenharmony_ci unsigned int page_type = iostat_ctx->type; 21762306a36Sopenharmony_ci struct f2fs_sb_info *sbi = iostat_ctx->sbi; 21862306a36Sopenharmony_ci struct iostat_lat_info *io_lat = sbi->iostat_io_lat; 21962306a36Sopenharmony_ci unsigned long flags; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (!sbi->iostat_enable) 22262306a36Sopenharmony_ci return; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci ts_diff = jiffies - iostat_ctx->submit_ts; 22562306a36Sopenharmony_ci if (page_type == META_FLUSH) { 22662306a36Sopenharmony_ci page_type = META; 22762306a36Sopenharmony_ci } else if (page_type >= NR_PAGE_TYPE) { 22862306a36Sopenharmony_ci f2fs_warn(sbi, "%s: %d over NR_PAGE_TYPE", __func__, page_type); 22962306a36Sopenharmony_ci return; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci spin_lock_irqsave(&sbi->iostat_lat_lock, flags); 23362306a36Sopenharmony_ci io_lat->sum_lat[lat_type][page_type] += ts_diff; 23462306a36Sopenharmony_ci io_lat->bio_cnt[lat_type][page_type]++; 23562306a36Sopenharmony_ci if (ts_diff > io_lat->peak_lat[lat_type][page_type]) 23662306a36Sopenharmony_ci io_lat->peak_lat[lat_type][page_type] = ts_diff; 23762306a36Sopenharmony_ci spin_unlock_irqrestore(&sbi->iostat_lat_lock, flags); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_civoid iostat_update_and_unbind_ctx(struct bio *bio) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct bio_iostat_ctx *iostat_ctx = bio->bi_private; 24362306a36Sopenharmony_ci enum iostat_lat_type lat_type; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (op_is_write(bio_op(bio))) { 24662306a36Sopenharmony_ci lat_type = bio->bi_opf & REQ_SYNC ? 24762306a36Sopenharmony_ci WRITE_SYNC_IO : WRITE_ASYNC_IO; 24862306a36Sopenharmony_ci bio->bi_private = iostat_ctx->sbi; 24962306a36Sopenharmony_ci } else { 25062306a36Sopenharmony_ci lat_type = READ_IO; 25162306a36Sopenharmony_ci bio->bi_private = iostat_ctx->post_read_ctx; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci __update_iostat_latency(iostat_ctx, lat_type); 25562306a36Sopenharmony_ci mempool_free(iostat_ctx, bio_iostat_ctx_pool); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_civoid iostat_alloc_and_bind_ctx(struct f2fs_sb_info *sbi, 25962306a36Sopenharmony_ci struct bio *bio, struct bio_post_read_ctx *ctx) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct bio_iostat_ctx *iostat_ctx; 26262306a36Sopenharmony_ci /* Due to the mempool, this never fails. */ 26362306a36Sopenharmony_ci iostat_ctx = mempool_alloc(bio_iostat_ctx_pool, GFP_NOFS); 26462306a36Sopenharmony_ci iostat_ctx->sbi = sbi; 26562306a36Sopenharmony_ci iostat_ctx->submit_ts = 0; 26662306a36Sopenharmony_ci iostat_ctx->type = 0; 26762306a36Sopenharmony_ci iostat_ctx->post_read_ctx = ctx; 26862306a36Sopenharmony_ci bio->bi_private = iostat_ctx; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ciint __init f2fs_init_iostat_processing(void) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci bio_iostat_ctx_cache = 27462306a36Sopenharmony_ci kmem_cache_create("f2fs_bio_iostat_ctx", 27562306a36Sopenharmony_ci sizeof(struct bio_iostat_ctx), 0, 0, NULL); 27662306a36Sopenharmony_ci if (!bio_iostat_ctx_cache) 27762306a36Sopenharmony_ci goto fail; 27862306a36Sopenharmony_ci bio_iostat_ctx_pool = 27962306a36Sopenharmony_ci mempool_create_slab_pool(NUM_PREALLOC_IOSTAT_CTXS, 28062306a36Sopenharmony_ci bio_iostat_ctx_cache); 28162306a36Sopenharmony_ci if (!bio_iostat_ctx_pool) 28262306a36Sopenharmony_ci goto fail_free_cache; 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cifail_free_cache: 28662306a36Sopenharmony_ci kmem_cache_destroy(bio_iostat_ctx_cache); 28762306a36Sopenharmony_cifail: 28862306a36Sopenharmony_ci return -ENOMEM; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_civoid f2fs_destroy_iostat_processing(void) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci mempool_destroy(bio_iostat_ctx_pool); 29462306a36Sopenharmony_ci kmem_cache_destroy(bio_iostat_ctx_cache); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ciint f2fs_init_iostat(struct f2fs_sb_info *sbi) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci /* init iostat info */ 30062306a36Sopenharmony_ci spin_lock_init(&sbi->iostat_lock); 30162306a36Sopenharmony_ci spin_lock_init(&sbi->iostat_lat_lock); 30262306a36Sopenharmony_ci sbi->iostat_enable = false; 30362306a36Sopenharmony_ci sbi->iostat_period_ms = DEFAULT_IOSTAT_PERIOD_MS; 30462306a36Sopenharmony_ci sbi->iostat_io_lat = f2fs_kzalloc(sbi, sizeof(struct iostat_lat_info), 30562306a36Sopenharmony_ci GFP_KERNEL); 30662306a36Sopenharmony_ci if (!sbi->iostat_io_lat) 30762306a36Sopenharmony_ci return -ENOMEM; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_civoid f2fs_destroy_iostat(struct f2fs_sb_info *sbi) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci kfree(sbi->iostat_io_lat); 31562306a36Sopenharmony_ci} 316