123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Samsung Block Statistics
- *
- * Copyright (C) 2021 Manjong Lee <[email protected]>
- * Copyright (C) 2021 Junho Kim <[email protected]>
- * Copyright (C) 2021 Changheun Lee <[email protected]>
- * Copyright (C) 2021 Seunghwan Hyun <[email protected]>
- * Copyright (C) 2021 Tran Xuan Nam <[email protected]>
- */
- #include <linux/sysfs.h>
- #include <linux/module.h>
- #include <linux/blk_types.h>
- #include <linux/blkdev.h>
- #include <linux/blk-mq.h>
- #include <linux/part_stat.h>
- #include "blk-sec.h"
- struct accumulated_stat {
- struct timespec64 uptime;
- unsigned long sectors[3]; /* READ, WRITE, DISCARD */
- unsigned long ios[3];
- unsigned long iot;
- };
- static struct accumulated_stat old, new;
- extern int blk_sec_stat_pio_init(struct kobject *kobj);
- extern void blk_sec_stat_pio_exit(struct kobject *kobj);
- extern struct pio_node *get_pio_node(struct request *rq);
- extern void update_pio_node(struct request *rq,
- unsigned int data_size, struct pio_node *pio);
- extern void put_pio_node(struct pio_node *pio);
- extern int blk_sec_stat_traffic_init(struct kobject *kobj);
- extern void blk_sec_stat_traffic_exit(struct kobject *kobj);
- extern void blk_sec_stat_traffic_update(struct request *rq,
- unsigned int data_size);
- void blk_sec_stat_account_init(struct request_queue *q)
- {
- if (!blk_sec_internal_disk())
- pr_err("%s: Can't find internal disk info!", __func__);
- }
- EXPORT_SYMBOL(blk_sec_stat_account_init);
- void blk_sec_stat_account_exit(struct elevator_queue *eq)
- {
- }
- EXPORT_SYMBOL(blk_sec_stat_account_exit);
- #define UNSIGNED_DIFF(n, o) (((n) >= (o)) ? ((n) - (o)) : ((n) + (0 - (o))))
- #define SECTORS2KB(x) ((x) / 2)
- static inline void get_monotonic_boottime(struct timespec64 *ts)
- {
- *ts = ktime_to_timespec64(ktime_get_boottime());
- }
- static ssize_t diskios_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
- {
- struct gendisk *gd = blk_sec_internal_disk();
- struct block_device *bdev;
- long hours;
- int ret;
- if (unlikely(!gd))
- return -EINVAL;
- bdev = gd->part0;
- new.ios[STAT_READ] = part_stat_read(bdev, ios[STAT_READ]);
- new.ios[STAT_WRITE] = part_stat_read(bdev, ios[STAT_WRITE]);
- new.ios[STAT_DISCARD] = part_stat_read(bdev, ios[STAT_DISCARD]);
- new.sectors[STAT_READ] = part_stat_read(bdev, sectors[STAT_READ]);
- new.sectors[STAT_WRITE] = part_stat_read(bdev, sectors[STAT_WRITE]);
- new.sectors[STAT_DISCARD] = part_stat_read(bdev, sectors[STAT_DISCARD]);
- new.iot = jiffies_to_msecs(part_stat_read(bdev, io_ticks)) / 1000;
- get_monotonic_boottime(&(new.uptime));
- hours = (new.uptime.tv_sec - old.uptime.tv_sec) / 60;
- hours = (hours + 30) / 60;
- ret = sprintf(buf, "\"ReadC\":\"%lu\",\"ReadKB\":\"%lu\","
- "\"WriteC\":\"%lu\",\"WriteKB\":\"%lu\","
- "\"DiscardC\":\"%lu\",\"DiscardKB\":\"%lu\","
- "\"IOT\":\"%lu\","
- "\"Hours\":\"%ld\"\n",
- UNSIGNED_DIFF(new.ios[STAT_READ], old.ios[STAT_READ]),
- SECTORS2KB(UNSIGNED_DIFF(new.sectors[STAT_READ], old.sectors[STAT_READ])),
- UNSIGNED_DIFF(new.ios[STAT_WRITE], old.ios[STAT_WRITE]),
- SECTORS2KB(UNSIGNED_DIFF(new.sectors[STAT_WRITE], old.sectors[STAT_WRITE])),
- UNSIGNED_DIFF(new.ios[STAT_DISCARD], old.ios[STAT_DISCARD]),
- SECTORS2KB(UNSIGNED_DIFF(new.sectors[STAT_DISCARD], old.sectors[STAT_DISCARD])),
- UNSIGNED_DIFF(new.iot, old.iot),
- hours);
- old.ios[STAT_READ] = new.ios[STAT_READ];
- old.ios[STAT_WRITE] = new.ios[STAT_WRITE];
- old.ios[STAT_DISCARD] = new.ios[STAT_DISCARD];
- old.sectors[STAT_READ] = new.sectors[STAT_READ];
- old.sectors[STAT_WRITE] = new.sectors[STAT_WRITE];
- old.sectors[STAT_DISCARD] = new.sectors[STAT_DISCARD];
- old.uptime = new.uptime;
- old.iot = new.iot;
- return ret;
- }
- static inline bool may_account_rq(struct request *rq)
- {
- struct gendisk *gd = blk_sec_internal_disk();
- if (unlikely(!gd))
- return false;
- if (gd->queue != rq->q)
- return false;
- return true;
- }
- void blk_sec_stat_account_io_prepare(struct request *rq, void *ptr_pio)
- {
- if (unlikely(!may_account_rq(rq)))
- return;
- *(struct pio_node **)ptr_pio = get_pio_node(rq);
- }
- EXPORT_SYMBOL(blk_sec_stat_account_io_prepare);
- void blk_sec_stat_account_io_complete(struct request *rq,
- unsigned int data_size, void *pio)
- {
- if (unlikely(!may_account_rq(rq)))
- return;
- blk_sec_stat_traffic_update(rq, data_size);
- update_pio_node(rq, data_size, (struct pio_node *)pio);
- }
- EXPORT_SYMBOL(blk_sec_stat_account_io_complete);
- void blk_sec_stat_account_io_finish(struct request *rq, void *ptr_pio)
- {
- if (unlikely(!may_account_rq(rq)))
- return;
- put_pio_node(*(struct pio_node **)ptr_pio);
- *(struct pio_node **)ptr_pio = NULL;
- }
- EXPORT_SYMBOL(blk_sec_stat_account_io_finish);
- static struct kobj_attribute diskios_attr = __ATTR(diskios, 0444, diskios_show, NULL);
- static const struct attribute *blk_sec_stat_attrs[] = {
- &diskios_attr.attr,
- NULL,
- };
- static struct kobject *blk_sec_stats_kobj;
- static int __init blk_sec_stats_init(void)
- {
- int retval;
- blk_sec_stats_kobj = kobject_create_and_add("blk_sec_stats", kernel_kobj);
- if (!blk_sec_stats_kobj)
- return -ENOMEM;
- retval = sysfs_create_files(blk_sec_stats_kobj, blk_sec_stat_attrs);
- if (retval) {
- kobject_put(blk_sec_stats_kobj);
- return retval;
- }
- retval = blk_sec_stat_pio_init(blk_sec_stats_kobj);
- if (retval)
- pr_err("%s: fail to initialize PIO sub module", __func__);
- retval = blk_sec_stat_traffic_init(blk_sec_stats_kobj);
- if (retval)
- pr_err("%s: fail to initialize TRAFFIC sub module", __func__);
- return 0;
- }
- static void __exit blk_sec_stats_exit(void)
- {
- blk_sec_stat_traffic_exit(blk_sec_stats_kobj);
- blk_sec_stat_pio_exit(blk_sec_stats_kobj);
- sysfs_remove_files(blk_sec_stats_kobj, blk_sec_stat_attrs);
- kobject_put(blk_sec_stats_kobj);
- }
- module_init(blk_sec_stats_init);
- module_exit(blk_sec_stats_exit);
- MODULE_LICENSE("GPL v2");
- MODULE_AUTHOR("Manjong Lee <[email protected]>");
- MODULE_DESCRIPTION("Samsung block layer statistics module for various purposes");
- MODULE_VERSION("1.0");
|