mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
287 lines
7.5 KiB
287 lines
7.5 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* f2fs iostat support |
|
* |
|
* Copyright 2021 Google LLC |
|
* Author: Daeho Jeong <[email protected]> |
|
*/ |
|
|
|
#include <linux/fs.h> |
|
#include <linux/f2fs_fs.h> |
|
#include <linux/seq_file.h> |
|
|
|
#include "f2fs.h" |
|
#include "iostat.h" |
|
#include <trace/events/f2fs.h> |
|
|
|
#define NUM_PREALLOC_IOSTAT_CTXS 128 |
|
static struct kmem_cache *bio_iostat_ctx_cache; |
|
static mempool_t *bio_iostat_ctx_pool; |
|
|
|
int __maybe_unused iostat_info_seq_show(struct seq_file *seq, void *offset) |
|
{ |
|
struct super_block *sb = seq->private; |
|
struct f2fs_sb_info *sbi = F2FS_SB(sb); |
|
time64_t now = ktime_get_real_seconds(); |
|
|
|
if (!sbi->iostat_enable) |
|
return 0; |
|
|
|
seq_printf(seq, "time: %-16llu\n", now); |
|
|
|
/* print app write IOs */ |
|
seq_puts(seq, "[WRITE]\n"); |
|
seq_printf(seq, "app buffered: %-16llu\n", |
|
sbi->rw_iostat[APP_BUFFERED_IO]); |
|
seq_printf(seq, "app direct: %-16llu\n", |
|
sbi->rw_iostat[APP_DIRECT_IO]); |
|
seq_printf(seq, "app mapped: %-16llu\n", |
|
sbi->rw_iostat[APP_MAPPED_IO]); |
|
|
|
/* print fs write IOs */ |
|
seq_printf(seq, "fs data: %-16llu\n", |
|
sbi->rw_iostat[FS_DATA_IO]); |
|
seq_printf(seq, "fs node: %-16llu\n", |
|
sbi->rw_iostat[FS_NODE_IO]); |
|
seq_printf(seq, "fs meta: %-16llu\n", |
|
sbi->rw_iostat[FS_META_IO]); |
|
seq_printf(seq, "fs gc data: %-16llu\n", |
|
sbi->rw_iostat[FS_GC_DATA_IO]); |
|
seq_printf(seq, "fs gc node: %-16llu\n", |
|
sbi->rw_iostat[FS_GC_NODE_IO]); |
|
seq_printf(seq, "fs cp data: %-16llu\n", |
|
sbi->rw_iostat[FS_CP_DATA_IO]); |
|
seq_printf(seq, "fs cp node: %-16llu\n", |
|
sbi->rw_iostat[FS_CP_NODE_IO]); |
|
seq_printf(seq, "fs cp meta: %-16llu\n", |
|
sbi->rw_iostat[FS_CP_META_IO]); |
|
|
|
/* print app read IOs */ |
|
seq_puts(seq, "[READ]\n"); |
|
seq_printf(seq, "app buffered: %-16llu\n", |
|
sbi->rw_iostat[APP_BUFFERED_READ_IO]); |
|
seq_printf(seq, "app direct: %-16llu\n", |
|
sbi->rw_iostat[APP_DIRECT_READ_IO]); |
|
seq_printf(seq, "app mapped: %-16llu\n", |
|
sbi->rw_iostat[APP_MAPPED_READ_IO]); |
|
|
|
/* print fs read IOs */ |
|
seq_printf(seq, "fs data: %-16llu\n", |
|
sbi->rw_iostat[FS_DATA_READ_IO]); |
|
seq_printf(seq, "fs gc data: %-16llu\n", |
|
sbi->rw_iostat[FS_GDATA_READ_IO]); |
|
seq_printf(seq, "fs compr_data: %-16llu\n", |
|
sbi->rw_iostat[FS_CDATA_READ_IO]); |
|
seq_printf(seq, "fs node: %-16llu\n", |
|
sbi->rw_iostat[FS_NODE_READ_IO]); |
|
seq_printf(seq, "fs meta: %-16llu\n", |
|
sbi->rw_iostat[FS_META_READ_IO]); |
|
|
|
/* print other IOs */ |
|
seq_puts(seq, "[OTHER]\n"); |
|
seq_printf(seq, "fs discard: %-16llu\n", |
|
sbi->rw_iostat[FS_DISCARD]); |
|
|
|
return 0; |
|
} |
|
|
|
static inline void __record_iostat_latency(struct f2fs_sb_info *sbi) |
|
{ |
|
int io, idx = 0; |
|
unsigned int cnt; |
|
struct f2fs_iostat_latency iostat_lat[MAX_IO_TYPE][NR_PAGE_TYPE]; |
|
struct iostat_lat_info *io_lat = sbi->iostat_io_lat; |
|
|
|
spin_lock_irq(&sbi->iostat_lat_lock); |
|
for (idx = 0; idx < MAX_IO_TYPE; idx++) { |
|
for (io = 0; io < NR_PAGE_TYPE; io++) { |
|
cnt = io_lat->bio_cnt[idx][io]; |
|
iostat_lat[idx][io].peak_lat = |
|
jiffies_to_msecs(io_lat->peak_lat[idx][io]); |
|
iostat_lat[idx][io].cnt = cnt; |
|
iostat_lat[idx][io].avg_lat = cnt ? |
|
jiffies_to_msecs(io_lat->sum_lat[idx][io]) / cnt : 0; |
|
io_lat->sum_lat[idx][io] = 0; |
|
io_lat->peak_lat[idx][io] = 0; |
|
io_lat->bio_cnt[idx][io] = 0; |
|
} |
|
} |
|
spin_unlock_irq(&sbi->iostat_lat_lock); |
|
|
|
trace_f2fs_iostat_latency(sbi, iostat_lat); |
|
} |
|
|
|
static inline void f2fs_record_iostat(struct f2fs_sb_info *sbi) |
|
{ |
|
unsigned long long iostat_diff[NR_IO_TYPE]; |
|
int i; |
|
|
|
if (time_is_after_jiffies(sbi->iostat_next_period)) |
|
return; |
|
|
|
/* Need double check under the lock */ |
|
spin_lock(&sbi->iostat_lock); |
|
if (time_is_after_jiffies(sbi->iostat_next_period)) { |
|
spin_unlock(&sbi->iostat_lock); |
|
return; |
|
} |
|
sbi->iostat_next_period = jiffies + |
|
msecs_to_jiffies(sbi->iostat_period_ms); |
|
|
|
for (i = 0; i < NR_IO_TYPE; i++) { |
|
iostat_diff[i] = sbi->rw_iostat[i] - |
|
sbi->prev_rw_iostat[i]; |
|
sbi->prev_rw_iostat[i] = sbi->rw_iostat[i]; |
|
} |
|
spin_unlock(&sbi->iostat_lock); |
|
|
|
trace_f2fs_iostat(sbi, iostat_diff); |
|
|
|
__record_iostat_latency(sbi); |
|
} |
|
|
|
void f2fs_reset_iostat(struct f2fs_sb_info *sbi) |
|
{ |
|
struct iostat_lat_info *io_lat = sbi->iostat_io_lat; |
|
int i; |
|
|
|
spin_lock(&sbi->iostat_lock); |
|
for (i = 0; i < NR_IO_TYPE; i++) { |
|
sbi->rw_iostat[i] = 0; |
|
sbi->prev_rw_iostat[i] = 0; |
|
} |
|
spin_unlock(&sbi->iostat_lock); |
|
|
|
spin_lock_irq(&sbi->iostat_lat_lock); |
|
memset(io_lat, 0, sizeof(struct iostat_lat_info)); |
|
spin_unlock_irq(&sbi->iostat_lat_lock); |
|
} |
|
|
|
void f2fs_update_iostat(struct f2fs_sb_info *sbi, |
|
enum iostat_type type, unsigned long long io_bytes) |
|
{ |
|
if (!sbi->iostat_enable) |
|
return; |
|
|
|
spin_lock(&sbi->iostat_lock); |
|
sbi->rw_iostat[type] += io_bytes; |
|
|
|
if (type == APP_WRITE_IO || type == APP_DIRECT_IO) |
|
sbi->rw_iostat[APP_BUFFERED_IO] = |
|
sbi->rw_iostat[APP_WRITE_IO] - |
|
sbi->rw_iostat[APP_DIRECT_IO]; |
|
|
|
if (type == APP_READ_IO || type == APP_DIRECT_READ_IO) |
|
sbi->rw_iostat[APP_BUFFERED_READ_IO] = |
|
sbi->rw_iostat[APP_READ_IO] - |
|
sbi->rw_iostat[APP_DIRECT_READ_IO]; |
|
spin_unlock(&sbi->iostat_lock); |
|
|
|
f2fs_record_iostat(sbi); |
|
} |
|
|
|
static inline void __update_iostat_latency(struct bio_iostat_ctx *iostat_ctx, |
|
int rw, bool is_sync) |
|
{ |
|
unsigned long ts_diff; |
|
unsigned int iotype = iostat_ctx->type; |
|
unsigned long flags; |
|
struct f2fs_sb_info *sbi = iostat_ctx->sbi; |
|
struct iostat_lat_info *io_lat = sbi->iostat_io_lat; |
|
int idx; |
|
|
|
if (!sbi->iostat_enable) |
|
return; |
|
|
|
ts_diff = jiffies - iostat_ctx->submit_ts; |
|
if (iotype >= META_FLUSH) |
|
iotype = META; |
|
|
|
if (rw == 0) { |
|
idx = READ_IO; |
|
} else { |
|
if (is_sync) |
|
idx = WRITE_SYNC_IO; |
|
else |
|
idx = WRITE_ASYNC_IO; |
|
} |
|
|
|
spin_lock_irqsave(&sbi->iostat_lat_lock, flags); |
|
io_lat->sum_lat[idx][iotype] += ts_diff; |
|
io_lat->bio_cnt[idx][iotype]++; |
|
if (ts_diff > io_lat->peak_lat[idx][iotype]) |
|
io_lat->peak_lat[idx][iotype] = ts_diff; |
|
spin_unlock_irqrestore(&sbi->iostat_lat_lock, flags); |
|
} |
|
|
|
void iostat_update_and_unbind_ctx(struct bio *bio, int rw) |
|
{ |
|
struct bio_iostat_ctx *iostat_ctx = bio->bi_private; |
|
bool is_sync = bio->bi_opf & REQ_SYNC; |
|
|
|
if (rw == 0) |
|
bio->bi_private = iostat_ctx->post_read_ctx; |
|
else |
|
bio->bi_private = iostat_ctx->sbi; |
|
__update_iostat_latency(iostat_ctx, rw, is_sync); |
|
mempool_free(iostat_ctx, bio_iostat_ctx_pool); |
|
} |
|
|
|
void iostat_alloc_and_bind_ctx(struct f2fs_sb_info *sbi, |
|
struct bio *bio, struct bio_post_read_ctx *ctx) |
|
{ |
|
struct bio_iostat_ctx *iostat_ctx; |
|
/* Due to the mempool, this never fails. */ |
|
iostat_ctx = mempool_alloc(bio_iostat_ctx_pool, GFP_NOFS); |
|
iostat_ctx->sbi = sbi; |
|
iostat_ctx->submit_ts = 0; |
|
iostat_ctx->type = 0; |
|
iostat_ctx->post_read_ctx = ctx; |
|
bio->bi_private = iostat_ctx; |
|
} |
|
|
|
int __init f2fs_init_iostat_processing(void) |
|
{ |
|
bio_iostat_ctx_cache = |
|
kmem_cache_create("f2fs_bio_iostat_ctx", |
|
sizeof(struct bio_iostat_ctx), 0, 0, NULL); |
|
if (!bio_iostat_ctx_cache) |
|
goto fail; |
|
bio_iostat_ctx_pool = |
|
mempool_create_slab_pool(NUM_PREALLOC_IOSTAT_CTXS, |
|
bio_iostat_ctx_cache); |
|
if (!bio_iostat_ctx_pool) |
|
goto fail_free_cache; |
|
return 0; |
|
|
|
fail_free_cache: |
|
kmem_cache_destroy(bio_iostat_ctx_cache); |
|
fail: |
|
return -ENOMEM; |
|
} |
|
|
|
void f2fs_destroy_iostat_processing(void) |
|
{ |
|
mempool_destroy(bio_iostat_ctx_pool); |
|
kmem_cache_destroy(bio_iostat_ctx_cache); |
|
} |
|
|
|
int f2fs_init_iostat(struct f2fs_sb_info *sbi) |
|
{ |
|
/* init iostat info */ |
|
spin_lock_init(&sbi->iostat_lock); |
|
spin_lock_init(&sbi->iostat_lat_lock); |
|
sbi->iostat_enable = false; |
|
sbi->iostat_period_ms = DEFAULT_IOSTAT_PERIOD_MS; |
|
sbi->iostat_io_lat = f2fs_kzalloc(sbi, sizeof(struct iostat_lat_info), |
|
GFP_KERNEL); |
|
if (!sbi->iostat_io_lat) |
|
return -ENOMEM; |
|
|
|
return 0; |
|
} |
|
|
|
void f2fs_destroy_iostat(struct f2fs_sb_info *sbi) |
|
{ |
|
kfree(sbi->iostat_io_lat); |
|
}
|
|
|